From 09a0c1c0b1a51739227a22339043ea6cc14a3272 Mon Sep 17 00:00:00 2001 From: HelperWesley <72082749+HelperWesley@users.noreply.github.com> Date: Thu, 23 Jul 2026 09:42:20 -0230 Subject: [PATCH] Starter Game Juice Update - And New assets Updated most starters in the engine with reactive buttons, sound effects, and particles to match the existing starters with those in them. Also replaced the 2D art assets used in those starters with newer assets that better align with the GDevelop's art direction to match the original starters. --- .../Flat dark joystick border horizontal.png | Bin 11053 -> 4013 bytes .../Flat dark joystick border vertical.png | Bin 10958 -> 3944 bytes .../assets/Flat dark joystick thumb.png | Bin 1044 -> 2939 bytes .../assets/StartingGroundTilemap.png | Bin 1758 -> 1847 bytes .../assets/StartingObstacle.png | Bin 1451 -> 1796 bytes .../assets/StartingWall.png | Bin 5306 -> 307 bytes .../starting-2d-car-racing.json | 9 +- .../Flat dark joystick border horizontal.png | Bin 1177 -> 4013 bytes .../Flat dark joystick border vertical.png | Bin 1205 -> 3944 bytes .../assets/Flat dark joystick thumb.png | Bin 614 -> 2939 bytes .../assets/StartingGroundTilemap.png | Bin 649 -> 1847 bytes .../assets/StartingObstacle.png | Bin 505 -> 1796 bytes .../starting-2d-driving.json | 2 +- .../assets/Background.png | Bin 198 -> 381 bytes .../assets/Collision.wav | Bin 0 -> 18870 bytes .../assets/Flat dark joystick border.png | Bin 939 -> 3751 bytes .../assets/Flat dark joystick thumb.png | Bin 614 -> 2939 bytes .../assets/StartingBullet.png | Bin 432 -> 1919 bytes .../assets/StartingGround.png | Bin 414 -> 668 bytes .../assets/StartingPlayer.png | Bin 467 -> 2406 bytes .../assets/StartingTarget.png | Bin 592 -> 7789 bytes .../assets/Target round button.png | Bin 789 -> 4371 bytes .../assets/Top arrow button.png | Bin 641 -> 3635 bytes .../assets/starting_bulletfire.wav | Bin 0 -> 6148 bytes .../starting-2d-platformer-shooter.json | 4721 ++++- .../assets/Flat dark joystick border.png | Bin 939 -> 3751 bytes .../assets/Flat dark joystick thumb.png | Bin 614 -> 2939 bytes .../assets/StarterPushable.png | Bin 382 -> 428 bytes .../assets/StarterToggle_Keyboard.png | Bin 1276 -> 3413 bytes .../assets/StarterToggle_Touch.png | Bin 1129 -> 3329 bytes .../assets/StartingGround.png | Bin 4512 -> 2824 bytes .../assets/StartingWall.png | Bin 378 -> 429 bytes .../assets/Top arrow button.png | Bin 641 -> 3635 bytes .../starting-3D-platformer.json | 7615 +++++++- .../Flat dark joystick border horizontal.png | Bin 0 -> 4013 bytes .../Flat dark joystick border vertical.png | Bin 0 -> 3944 bytes .../assets/Flat dark joystick border.png | Bin 939 -> 0 bytes .../assets/Flat dark joystick thumb.png | Bin 614 -> 2939 bytes .../assets/StartingCheckeredPattern.png | Bin 192 -> 488 bytes .../assets/StartingFinishLine.png | Bin 807 -> 459 bytes .../assets/StartingGround.png | Bin 4512 -> 2824 bytes .../assets/StartingWall.png | Bin 0 -> 548 bytes .../starting-3d-car-racing.json | 101 +- .../assets/StartingTiledGround.png | Bin 754 -> 363 bytes .../assets/StartingTiles.png | Bin 969 -> 488 bytes .../assets/starting_woodthump.wav | Bin 0 -> 35904 bytes .../starting-3d-draggable-tiles.json | 200 +- .../Flat dark joystick border horizontal.png | Bin 0 -> 4013 bytes .../Flat dark joystick border vertical.png | Bin 0 -> 3944 bytes .../assets/Flat dark joystick border.png | Bin 939 -> 0 bytes .../assets/Flat dark joystick thumb.png | Bin 614 -> 2939 bytes .../assets/StartingCheckeredPattern.png | Bin 192 -> 488 bytes .../assets/StartingFinishLine.png | Bin 807 -> 0 bytes .../assets/StartingGround.png | Bin 4512 -> 2824 bytes .../assets/StartingWall.png | Bin 0 -> 548 bytes .../starting-3d-driving.json | 58 +- .../assets/Background.png | Bin 277 -> 381 bytes .../assets/SpawnPoint.png | Bin 877 -> 515 bytes .../assets/StartingCollision.wav | Bin 0 -> 18870 bytes .../assets/StartingGround.png | Bin 1400 -> 668 bytes .../assets/Top arrow button.png | Bin 926 -> 3635 bytes .../starting-3d-endless-runner.json | 6819 +++++++- .../Flat dark joystick border vertical.png | Bin 10958 -> 3944 bytes .../assets/Flat dark joystick border.png | Bin 1632 -> 3751 bytes .../assets/Flat dark joystick thumb.png | Bin 1044 -> 2939 bytes .../assets/StartingTiledGround.png | Bin 1028 -> 363 bytes .../starting-3d-flight-sim.json | 2 +- .../assets/Border.png | Bin 6126 -> 1164 bytes .../assets/Border2.png | Bin 5983 -> 1031 bytes .../assets/DirtParticle.png | Bin 0 -> 969 bytes .../assets/StartingCollisionArea.png | Bin 839 -> 776 bytes .../assets/StartingTiledGround.png | Bin 1028 -> 363 bytes .../assets/StartingWall.png | Bin 1234 -> 1720 bytes .../assets/StartingWater.png | Bin 2748 -> 2801 bytes .../assets/starter_pop.wav | Bin 0 -> 43564 bytes .../assets/starting_bookopen.wav | Bin 0 -> 39550 bytes ...starting-3d-point-and-click-adventure.json | 11418 +++++++++++- .../starting-3d-rts-unit-selection.json | 2 +- .../assets/Collision.wav | Bin 0 -> 18870 bytes .../assets/DirtParticle.png | Bin 0 -> 969 bytes .../assets/Flat dark joystick border.png | Bin 1632 -> 3751 bytes .../assets/Flat dark joystick thumb.png | Bin 1044 -> 2939 bytes .../assets/StartingObstacle.png | Bin 871 -> 429 bytes .../assets/StartingTiledGround.png | Bin 1028 -> 363 bytes .../assets/starting_bulletfire.wav | Bin 0 -> 6148 bytes .../starting-3d-shootemup.json | 14542 +++++++++++++++- .../assets/Flat dark joystick border.png | Bin 939 -> 3751 bytes .../assets/Flat dark joystick thumb.png | Bin 614 -> 2939 bytes .../assets/StartingCheckeredPattern.png | Bin 192 -> 488 bytes .../assets/StartingFinishLine.png | Bin 807 -> 0 bytes .../assets/StartingGround.png | Bin 4512 -> 2824 bytes .../starting-3d-tank/assets/StartingWall.png | Bin 0 -> 429 bytes .../assets/Target round button.png | Bin 1207 -> 4371 bytes .../starting-3d-tank/starting-3d-tank.json | 46 +- .../assets/Flat dark joystick border.png | Bin 1632 -> 0 bytes .../assets/Flat dark joystick thumb.png | Bin 1044 -> 0 bytes .../assets/StartingButtonTower.png | Bin 2134 -> 1396 bytes .../assets/StartingButtonTree.png | Bin 2395 -> 1603 bytes .../assets/StartingButtonUnit.png | Bin 2225 -> 2303 bytes .../assets/StartingTiledGround.png | Bin 754 -> 363 bytes .../assets/starter_switch.wav | Bin 0 -> 68288 bytes .../assets/starting_woodthump.wav | Bin 0 -> 35904 bytes .../starting-3d-tile-placement.json | 59 +- .../assets/A button.png | Bin 1012 -> 3655 bytes .../assets/Border.png | Bin 6126 -> 1164 bytes .../assets/Border2.png | Bin 5983 -> 1031 bytes .../assets/DirtParticle.png | Bin 0 -> 969 bytes .../assets/Flat dark joystick border.png | Bin 939 -> 3751 bytes .../assets/Flat dark joystick thumb.png | Bin 614 -> 2939 bytes .../assets/StartingTiledGround.png | Bin 754 -> 363 bytes .../assets/StartingWall.png | Bin 378 -> 429 bytes .../assets/starter_pop.wav | Bin 0 -> 43564 bytes .../assets/starting_bookopen.wav | Bin 0 -> 39550 bytes .../starting-3d-top-down-rpg.json | 12001 ++++++++++++- .../assets/Collision.wav | Bin 0 -> 18870 bytes .../assets/DirtParticle.png | Bin 0 -> 969 bytes .../assets/Flat dark joystick border.png | Bin 1632 -> 3751 bytes .../assets/Flat dark joystick thumb.png | Bin 1044 -> 2939 bytes .../assets/StartingTiledGround.png | Bin 1028 -> 363 bytes .../assets/starting_bulletfire.wav | Bin 0 -> 6148 bytes .../starting-3d-twin-stick-shooter.json | 14495 ++++++++++++++- .../assets/Collision.wav | Bin 0 -> 18870 bytes .../assets/DirtParticle.png | Bin 0 -> 969 bytes .../assets/Flat dark joystick border.png | Bin 1632 -> 3751 bytes .../assets/Flat dark joystick thumb.png | Bin 1044 -> 2939 bytes .../assets/StartingTiledGround.png | Bin 8801 -> 363 bytes .../assets/starting_bulletfire.wav | Bin 0 -> 6148 bytes .../starting-3d-vampire-survivor.json | 14525 ++++++++++++++- .../starting-beatemup/assets/Collision.wav | Bin 0 -> 18870 bytes .../assets/Flat dark joystick border.png | Bin 939 -> 3751 bytes .../assets/Flat dark joystick thumb.png | Bin 669 -> 2939 bytes .../assets/StartingEnemy.png | Bin 1432 -> 5450 bytes .../assets/StartingGroundTilemap.png | Bin 0 -> 1847 bytes .../assets/StartingObstacle.png | Bin 309 -> 429 bytes .../assets/StartingPlayer.png | Bin 1433 -> 5199 bytes .../assets/StartingTiledGround.png | Bin 1538 -> 0 bytes .../assets/Target round button.png | Bin 789 -> 4371 bytes .../starting-beatemup/starting-beatemup.json | 4883 +++++- .../starting-card-game/assets/Background.png | Bin 198 -> 381 bytes .../starting-card-game/assets/card_back.png | Bin 319 -> 12554 bytes .../assets/card_clubs_02.png | Bin 1281 -> 3355 bytes .../assets/card_clubs_03.png | Bin 1431 -> 2999 bytes .../assets/card_clubs_04.png | Bin 1370 -> 3965 bytes .../assets/card_clubs_05.png | Bin 1555 -> 3520 bytes .../assets/card_clubs_06.png | Bin 1607 -> 3412 bytes .../assets/card_clubs_07.png | Bin 1631 -> 3538 bytes .../assets/card_clubs_08.png | Bin 1849 -> 4075 bytes .../assets/card_clubs_09.png | Bin 1811 -> 3992 bytes .../assets/card_clubs_10.png | Bin 1899 -> 4646 bytes .../assets/card_clubs_A.png | Bin 1196 -> 3374 bytes .../assets/card_clubs_J.png | Bin 1305 -> 3834 bytes .../assets/card_clubs_K.png | Bin 1464 -> 3712 bytes .../assets/card_clubs_Q.png | Bin 1537 -> 3728 bytes .../assets/card_diamonds_02.png | Bin 1112 -> 2678 bytes .../assets/card_diamonds_03.png | Bin 1210 -> 2496 bytes .../assets/card_diamonds_04.png | Bin 1206 -> 2971 bytes .../assets/card_diamonds_05.png | Bin 1387 -> 2935 bytes .../assets/card_diamonds_06.png | Bin 1347 -> 2855 bytes .../assets/card_diamonds_07.png | Bin 1411 -> 2890 bytes .../assets/card_diamonds_08.png | Bin 1487 -> 3214 bytes .../assets/card_diamonds_09.png | Bin 1549 -> 3151 bytes .../assets/card_diamonds_10.png | Bin 1583 -> 3598 bytes .../assets/card_diamonds_A.png | Bin 1070 -> 2664 bytes .../assets/card_diamonds_J.png | Bin 1210 -> 3625 bytes .../assets/card_diamonds_K.png | Bin 1367 -> 3462 bytes .../assets/card_diamonds_Q.png | Bin 1433 -> 3529 bytes .../assets/card_hearts_02.png | Bin 1198 -> 2740 bytes .../assets/card_hearts_03.png | Bin 1285 -> 2522 bytes .../assets/card_hearts_04.png | Bin 1258 -> 3016 bytes .../assets/card_hearts_05.png | Bin 1425 -> 2929 bytes .../assets/card_hearts_06.png | Bin 1408 -> 2842 bytes .../assets/card_hearts_07.png | Bin 1487 -> 2849 bytes .../assets/card_hearts_08.png | Bin 1610 -> 3237 bytes .../assets/card_hearts_09.png | Bin 1630 -> 3128 bytes .../assets/card_hearts_10.png | Bin 1693 -> 3537 bytes .../assets/card_hearts_A.png | Bin 1101 -> 2757 bytes .../assets/card_hearts_J.png | Bin 1209 -> 3614 bytes .../assets/card_hearts_K.png | Bin 1384 -> 3484 bytes .../assets/card_hearts_Q.png | Bin 1487 -> 3529 bytes .../assets/card_spades_02.png | Bin 1192 -> 2896 bytes .../assets/card_spades_03.png | Bin 1334 -> 2678 bytes .../assets/card_spades_04.png | Bin 1269 -> 3359 bytes .../assets/card_spades_05.png | Bin 1440 -> 3215 bytes .../assets/card_spades_06.png | Bin 1467 -> 3144 bytes .../assets/card_spades_07.png | Bin 1515 -> 3162 bytes .../assets/card_spades_08.png | Bin 1722 -> 3573 bytes .../assets/card_spades_09.png | Bin 1660 -> 3380 bytes .../assets/card_spades_10.png | Bin 1636 -> 3954 bytes .../assets/card_spades_A.png | Bin 1125 -> 2933 bytes .../assets/card_spades_J.png | Bin 1256 -> 3694 bytes .../assets/card_spades_K.png | Bin 1461 -> 3543 bytes .../assets/card_spades_Q.png | Bin 1471 -> 3597 bytes .../starting-card-game.json | 6 +- .../starting-clicker/assets/Background.png | Bin 198 -> 381 bytes .../assets/StartingButtonBack.png | Bin 339 -> 1598 bytes .../assets/StartingClickerBall.png | Bin 1957 -> 14347 bytes .../assets/StartingProgressBorder.png | Bin 198 -> 262 bytes .../starting-clicker/starting-clicker.json | 22 +- .../assets/BoardTilemap.png | Bin 201 -> 405 bytes .../assets/StartingTower.png | Bin 517 -> 970 bytes .../assets/StartingTree.png | Bin 484 -> 1226 bytes .../assets/StartingUnit.png | Bin 479 -> 1902 bytes .../assets/starting_woodthump.wav | Bin 0 -> 35904 bytes .../starting-draggable-tiles.json | 64 +- .../assets/Background.png | Bin 198 -> 381 bytes .../assets/Collision.wav | Bin 0 -> 18870 bytes .../assets/SpawnPoint.png | Bin 267 -> 515 bytes .../assets/StartingGround.png | Bin 414 -> 668 bytes .../assets/StartingHazard.png | Bin 542 -> 2639 bytes .../assets/StartingPlayer.png | Bin 547 -> 2978 bytes .../assets/Top arrow button.png | Bin 641 -> 3635 bytes .../starting-endless-runner.json | 4580 ++++- .../assets/Flat dark joystick border.png | Bin 939 -> 3751 bytes .../assets/Flat dark joystick thumb.png | Bin 614 -> 2939 bytes .../assets/StarterToggle_Keyboard.png | Bin 1276 -> 3413 bytes .../assets/StarterToggle_Touch.png | Bin 1129 -> 3329 bytes .../assets/StartingGround.png | Bin 4512 -> 2824 bytes .../assets/StartingInventorySlot.png | Bin 6328 -> 1598 bytes .../assets/Target round button.png | Bin 1207 -> 4371 bytes .../assets/Top arrow button.png | Bin 641 -> 3635 bytes .../starting-first-person-farming.json | 58 +- .../assets/Flat dark joystick border.png | Bin 939 -> 3751 bytes .../assets/Flat dark joystick thumb.png | Bin 614 -> 2939 bytes .../assets/StarterPushable.png | Bin 382 -> 428 bytes .../assets/StarterToggle_Keyboard.png | Bin 1276 -> 3413 bytes .../assets/StarterToggle_Touch.png | Bin 1129 -> 3329 bytes .../assets/StartingCollisionArea.png | Bin 839 -> 776 bytes .../assets/StartingGround.png | Bin 4512 -> 2824 bytes .../assets/StartingWall.png | Bin 378 -> 429 bytes .../assets/Top arrow button.png | Bin 641 -> 3635 bytes .../starting-first-person-horror.json | 7341 +++++++- .../assets/Flat dark joystick border.png | Bin 939 -> 3751 bytes .../assets/Flat dark joystick thumb.png | Bin 614 -> 2939 bytes .../assets/StarterPushable.png | Bin 382 -> 428 bytes .../assets/StarterToggle_Keyboard.png | Bin 1276 -> 3413 bytes .../assets/StarterToggle_Touch.png | Bin 1129 -> 3329 bytes .../assets/StartingCollisionArea.png | Bin 839 -> 776 bytes .../assets/StartingGround.png | Bin 4512 -> 2824 bytes .../assets/StartingWall.png | Bin 378 -> 429 bytes .../assets/Target round button.png | Bin 1207 -> 4371 bytes .../assets/Top arrow button.png | Bin 641 -> 3635 bytes .../starting-first-person-shooter-horror.json | 61 +- .../assets/Flat dark joystick border.png | Bin 939 -> 3751 bytes .../assets/Flat dark joystick thumb.png | Bin 614 -> 2939 bytes .../assets/StarterPushable.png | Bin 382 -> 428 bytes .../assets/StarterToggle_Keyboard.png | Bin 1276 -> 3413 bytes .../assets/StarterToggle_Touch.png | Bin 1129 -> 3329 bytes .../assets/StartingGround.png | Bin 4512 -> 2824 bytes .../assets/StartingWall.png | Bin 378 -> 429 bytes .../assets/Target round button.png | Bin 1207 -> 4371 bytes .../assets/Top arrow button.png | Bin 641 -> 3635 bytes .../starting-first-person-shooter.json | 61 +- .../assets/Flat dark joystick border.png | Bin 939 -> 3751 bytes .../assets/Flat dark joystick thumb.png | Bin 614 -> 2939 bytes .../assets/StarterToggle_Keyboard.png | Bin 1276 -> 3413 bytes .../assets/StarterToggle_Touch.png | Bin 1129 -> 3329 bytes .../assets/StartingGround.png | Bin 648 -> 2824 bytes .../assets/StartingInventorySlot.png | Bin 6328 -> 1598 bytes .../assets/StartingWall.png | Bin 378 -> 429 bytes .../assets/Target round button.png | Bin 1207 -> 4371 bytes .../assets/Top arrow button.png | Bin 641 -> 3635 bytes ...arting-first-person-survival-crafting.json | 58 +- .../assets/Flat dark joystick border.png | Bin 939 -> 3751 bytes .../assets/Flat dark joystick thumb.png | Bin 614 -> 2939 bytes .../assets/StarterPushable.png | Bin 382 -> 428 bytes .../assets/StarterToggle_Keyboard.png | Bin 1276 -> 3413 bytes .../assets/StarterToggle_Touch.png | Bin 1129 -> 3329 bytes .../assets/StartingGround.png | Bin 4512 -> 2824 bytes .../assets/StartingWall.png | Bin 378 -> 429 bytes .../assets/Top arrow button.png | Bin 641 -> 3635 bytes .../starting-first-person.json | 7339 +++++++- .../assets/Background.png | Bin 198 -> 381 bytes .../starting-flappy-bird/assets/Collision.wav | Bin 0 -> 18870 bytes .../assets/SpawnPoint.png | Bin 267 -> 515 bytes .../assets/StartingGround.png | Bin 414 -> 668 bytes .../assets/StartingPlayer.png | Bin 547 -> 2978 bytes .../assets/StartingSpiketower.png | Bin 1135 -> 3378 bytes .../starting-flappy-bird.json | 4574 ++++- .../starting-physics/assets/Background.png | Bin 198 -> 381 bytes .../starting-physics/assets/StartingBall.png | Bin 642 -> 2569 bytes .../starting-physics/assets/StartingBlock.png | Bin 310 -> 1055 bytes .../assets/StartingBlock_Heavy.png | Bin 257 -> 517 bytes .../assets/StartingGround.png | Bin 414 -> 668 bytes .../starting-physics/starting-physics.json | 2 +- .../starting-platformer-pixel.json | 2 +- .../starting-platformer/assets/Background.png | Bin 198 -> 381 bytes .../assets/Flat dark joystick border.png | Bin 939 -> 3751 bytes .../assets/Flat dark joystick thumb.png | Bin 614 -> 2939 bytes .../assets/StartingGround.png | Bin 414 -> 668 bytes .../assets/StartingPlayer.png | Bin 547 -> 2978 bytes .../assets/Top arrow button.png | Bin 641 -> 3635 bytes .../starting-platformer.json | 153 +- .../assets/StartingGroundTilemap.png | Bin 649 -> 1847 bytes .../assets/StartingPlayer.png | Bin 749 -> 3394 bytes .../assets/StartingWall.png | Bin 150 -> 875 bytes .../assets/StartingWater.png | Bin 1465 -> 7990 bytes .../starting-point-and-click.json | 45 +- examples/starting-quiz/assets/Background.png | Bin 277 -> 381 bytes .../starting-quiz/assets/QuestionBackdrop.png | Bin 6328 -> 1598 bytes examples/starting-quiz/starting-quiz.json | 15 +- .../starting-rts-unit-selection.json | 2 +- .../starting-shootemup/assets/Collision.wav | Bin 0 -> 18870 bytes .../assets/DirtParticle.png | Bin 0 -> 969 bytes .../assets/Flat dark joystick border.png | Bin 1632 -> 3751 bytes .../assets/Flat dark joystick thumb.png | Bin 1044 -> 2939 bytes .../assets/StartingBullet.png | Bin 1033 -> 1919 bytes .../assets/StartingEnemyBullet.png | Bin 6461 -> 1885 bytes .../assets/StartingObstacle.png | Bin 871 -> 429 bytes .../assets/StartingPlayer.png | Bin 3009 -> 3394 bytes .../assets/starting_bulletfire.wav | Bin 0 -> 6148 bytes .../starting-shootemup.json | 4851 +++++- .../assets/StartingButtonTower.png | Bin 2134 -> 1396 bytes .../assets/StartingButtonTree.png | Bin 2395 -> 1603 bytes .../assets/StartingButtonUnit.png | Bin 2225 -> 2303 bytes .../assets/StartingGroundTilemap.png | Bin 1758 -> 1847 bytes .../assets/StartingTower.png | Bin 1774 -> 970 bytes .../assets/StartingTree.png | Bin 2078 -> 1226 bytes .../assets/StartingUnit.png | Bin 2087 -> 1902 bytes .../assets/starter_switch.wav | Bin 0 -> 68288 bytes .../assets/starting_woodthump.wav | Bin 0 -> 35904 bytes .../starting-tile-placement.json | 59 +- .../starting-top-down-farming.json | 6 +- .../starting-top-down-rpg.json | 2 +- .../assets/Flat dark joystick border.png | Bin 939 -> 3751 bytes .../assets/Flat dark joystick thumb.png | Bin 614 -> 2939 bytes .../assets/StartingGroundTilemap.png | Bin 649 -> 1847 bytes .../assets/StartingObstacle.png | Bin 505 -> 1796 bytes .../assets/StartingPlayer.png | Bin 749 -> 3394 bytes .../starting-top-down/assets/StartingWall.png | Bin 150 -> 875 bytes .../starting-top-down/starting-top-down.json | 120 +- .../assets/Collision.wav | Bin 0 -> 18870 bytes .../assets/DirtParticle.png | Bin 0 -> 969 bytes .../assets/Flat dark joystick border.png | Bin 939 -> 3751 bytes .../assets/Flat dark joystick thumb.png | Bin 614 -> 2939 bytes .../assets/StartingBullet.png | Bin 293 -> 1919 bytes .../assets/StartingPlayer.png | Bin 749 -> 3394 bytes .../assets/starting_bulletfire.wav | Bin 0 -> 6148 bytes .../starting-twin-stick-shooter.json | 4800 ++++- .../assets/Flat dark joystick border.png | Bin 939 -> 3751 bytes .../assets/Flat dark joystick thumb.png | Bin 614 -> 2939 bytes .../assets/StartingBullet.png | Bin 293 -> 1919 bytes .../assets/StartingPlayer.png | Bin 547 -> 2978 bytes .../starting-vampire-survivor.json | 26 +- 343 files changed, 124862 insertions(+), 883 deletions(-) create mode 100644 examples/starting-2d-platformer-shooter/assets/Collision.wav create mode 100644 examples/starting-2d-platformer-shooter/assets/starting_bulletfire.wav create mode 100644 examples/starting-3d-car-racing/assets/Flat dark joystick border horizontal.png create mode 100644 examples/starting-3d-car-racing/assets/Flat dark joystick border vertical.png delete mode 100644 examples/starting-3d-car-racing/assets/Flat dark joystick border.png create mode 100644 examples/starting-3d-car-racing/assets/StartingWall.png create mode 100644 examples/starting-3d-draggable-tiles/assets/starting_woodthump.wav create mode 100644 examples/starting-3d-driving/assets/Flat dark joystick border horizontal.png create mode 100644 examples/starting-3d-driving/assets/Flat dark joystick border vertical.png delete mode 100644 examples/starting-3d-driving/assets/Flat dark joystick border.png delete mode 100644 examples/starting-3d-driving/assets/StartingFinishLine.png create mode 100644 examples/starting-3d-driving/assets/StartingWall.png create mode 100644 examples/starting-3d-endless-runner/assets/StartingCollision.wav create mode 100644 examples/starting-3d-point-and-click-adventure/assets/DirtParticle.png create mode 100644 examples/starting-3d-point-and-click-adventure/assets/starter_pop.wav create mode 100644 examples/starting-3d-point-and-click-adventure/assets/starting_bookopen.wav create mode 100644 examples/starting-3d-shootemup/assets/Collision.wav create mode 100644 examples/starting-3d-shootemup/assets/DirtParticle.png create mode 100644 examples/starting-3d-shootemup/assets/starting_bulletfire.wav delete mode 100644 examples/starting-3d-tank/assets/StartingFinishLine.png create mode 100644 examples/starting-3d-tank/assets/StartingWall.png delete mode 100644 examples/starting-3d-tile-placement/assets/Flat dark joystick border.png delete mode 100644 examples/starting-3d-tile-placement/assets/Flat dark joystick thumb.png create mode 100644 examples/starting-3d-tile-placement/assets/starter_switch.wav create mode 100644 examples/starting-3d-tile-placement/assets/starting_woodthump.wav create mode 100644 examples/starting-3d-top-down-rpg/assets/DirtParticle.png create mode 100644 examples/starting-3d-top-down-rpg/assets/starter_pop.wav create mode 100644 examples/starting-3d-top-down-rpg/assets/starting_bookopen.wav create mode 100644 examples/starting-3d-twin-stick-shooter/assets/Collision.wav create mode 100644 examples/starting-3d-twin-stick-shooter/assets/DirtParticle.png create mode 100644 examples/starting-3d-twin-stick-shooter/assets/starting_bulletfire.wav create mode 100644 examples/starting-3d-vampire-survivor/assets/Collision.wav create mode 100644 examples/starting-3d-vampire-survivor/assets/DirtParticle.png create mode 100644 examples/starting-3d-vampire-survivor/assets/starting_bulletfire.wav create mode 100644 examples/starting-beatemup/assets/Collision.wav create mode 100644 examples/starting-beatemup/assets/StartingGroundTilemap.png delete mode 100644 examples/starting-beatemup/assets/StartingTiledGround.png create mode 100644 examples/starting-draggable-tiles/assets/starting_woodthump.wav create mode 100644 examples/starting-endless-runner/assets/Collision.wav create mode 100644 examples/starting-flappy-bird/assets/Collision.wav create mode 100644 examples/starting-shootemup/assets/Collision.wav create mode 100644 examples/starting-shootemup/assets/DirtParticle.png create mode 100644 examples/starting-shootemup/assets/starting_bulletfire.wav create mode 100644 examples/starting-tile-placement/assets/starter_switch.wav create mode 100644 examples/starting-tile-placement/assets/starting_woodthump.wav create mode 100644 examples/starting-twin-stick-shooter/assets/Collision.wav create mode 100644 examples/starting-twin-stick-shooter/assets/DirtParticle.png create mode 100644 examples/starting-twin-stick-shooter/assets/starting_bulletfire.wav diff --git a/examples/starting-2d-car-racing/assets/Flat dark joystick border horizontal.png b/examples/starting-2d-car-racing/assets/Flat dark joystick border horizontal.png index d1997e368d2763e1f8ab7ff00f142ae92dda30b3..4f772d873797453c44bb85fc6df481926391d4fa 100644 GIT binary patch delta 3973 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 O0-#D-if`nf2LBIOTpD}; diff --git a/examples/starting-2d-car-racing/assets/Flat dark joystick border vertical.png b/examples/starting-2d-car-racing/assets/Flat dark joystick border vertical.png index d468a61805f59debaa6701a0148db543fc9dc41a..ec315d4b6b8b57c6528ac9b1f3995a45acf69ad5 100644 GIT binary patch delta 3904 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 LNkvXXu0mjflp>}T delta 1022 zcmV zyp+Nh;58IdxZlj$Jks-6fY(q+p8T89>-ApBhkP54$Db&`{}j15xktIz)J8ls@b?s9 z9{9mv@Ykm^RWMB-@(kvH@AvzEBx@c2^>wO{Y^$C3K^KuW9oai^UE2XIUX&sFaK*sK=W{nW8pTH)DjRkD8u6f%9&P z_H?`!95P40;#H&q_SjPoLek}gJY{|;sVM+6CpkADuH4L&VMeSxd7EYkV44!m zkn%)5PST5_fSl7*(dLKI8AmKX*IAg4U?|_FW7U|Vg&0tyBoK_eB2su5 z)<7VlG(kal&s5nY1Y@9;NGt+MvtZe@tYwjq1d@i+zy0lU_5uE{mq1l$}fGUQi zwg|n54i1FQKrKGCH9dQrcUODy8Twd2#FgLYrK#y*(KdZcW8S*7nTut`U$PoP;88t^ z(3&P&f?iGRwGK$U-i(0*hnjytA9FkM!slH#Os3pCq>oE@3Xvhy!#X0(YP<26=)hgO zKA%fm`CbW$Kw#rSQq*maLPyL6(%60Bu{2BO5cj?z*1aH=9rOcx7OwN{Me^ok1B&gX}IZR1U@(T3@<-s zdYJ@ab&W)a5m?!lE4{B%vggY$UKN=>68~iJ;0a~^?L59PyMYSW zkT0Z3QTca1W2#V;F=|mgVbP@XhnR-A9U^9Ffxy8%7;tv;`epYR4yfNQ4HQk0UkPon zdx|nHlTn9SOd)xPIYe_i)$p&u@Ep1?ExO|ip>>jbqdi(B^wJTIG!HiIB^LT(*&S3r zqk$^q8`TP^i2j3-1CmevHKRHoEnisQD6}c>*Ms@!2>+Y#%fTnDEzycBt4|kGcX`eV z9dw)~0*2Hf1WwN(Axx%UJ+S>^sw(7~;3UA@>*jnoT6WGhNAZ}YE;~!-eHwG(_yh@^KDg>4Oh+`|!jXG5tFuj$S<~v)JxmcaeuIt}tLGn-M+JqJ;%n^7 zaErCWtBR#-(J*3WsN~Plm4VqAyB3NJCAP{s*;ukMfoXXcrZ3cUlneN`cS$F?EA()* zy`zWddWvpN;H|Aj)sX*VAGCd?pG}tEj=D2QSe0&$VAL?tt?Z<8cFAdfpL=OCXBnz? z61Njf=bc*&T<#k~wXO%9)u1VMI`JcANauwW&+Z@9aP6l3UDOB9QXo8R0j;rHxv`$; zH5=JShE+GRcV8z_3GM~HHiwkY*;orR+_I!WDB_rnl&Ly-l*B+kBHo9qbT00f708vDJ+SR@M zhK@PAlAwfY6({-W)~_0T+h}9B`18xb2vMj>+Ey>eI6iWbL1M2w*g!uh*SU8IiY{`GBi ze>m|S%9RqsZ+~g&mpU%Bxn1!~DUr}xk(+SEi>zXnloA)Vt@Mc}3dYFJoG%Zp$|4!7 zn!Yn+;%vkq(mmJyW!sgBwf?CiW0~w zJGS|0M7+1CaI?-c1FqDfyc%E2E@R+?)5YrO16z;-00ewAa2Nm`pbJdE#{~cOx~ONM z3cIbG7S6984Nrz(+SM;So;WdFEuxOU3-IU;mo!z6nz6R^KNHrL7W{`*QQvdF<*O7| z=ct{9JqYf8G*_z!qlVTolM)mO;>0i4j2DVU&9k;zW!Oc`bhy{u^(eHszM-b{p~2~^ yaw57DyBl`+)embCNXDGG=Pb>fn0c#yPy^P}>Mk5&n-yzW1JHpX$C{7CQ~w6AS|^$S literal 1758 zcmbVNdoJ#q>^@*X@BkGNR2jOd(8^B{6SHGwF`JX68|1l|*$B^5`mcQjyUBAeZTAd)SRQ^o7q(`y)zCem3gl ztGdft?Nftx6|?Bb&D)=m=K8dfEo9aA5K?W6Hy7*`ZmL+X*uH0r*7JFH$`x1*c!zUP zAr2OxL>+02Z=N>!X4>cY40N*%3+{60!rjec-HNKNf$h``9XHn zkda1B@NnA@@5Q){L>1902z^ntvyr3KfK*c`x~wBdvgU>yaLh;EJps@{%Z9&vs=mqu zGb!A*cBO1w#2uxRu1R+8==)@C5B0O=s^%8vBdeU$46)i;`N<8w2C(@l#=+WQ)rrQL zwhx>N~Bqu++Hy38}2>yvl__dGIA%R}gv6lJL}>qXqh z{+cD4%$gn^?-nIx-6najUkH?)B(hcP<4Bc5TQrfUn#yJacst)KLd(UU z40B3BxQoe_uq7%TclBlAx%%N(W9-wd8S&Gca0`o(-p5pT%uxnV<%8Q6gV<`NIeYF# z$~SkfqtyT#+>lFX_xnI5@r%tAz1K5Kok|1({PB{P4m(D>@@oa02OZ)zPJmr@kki=3 zdEzaJMv<3B#s*k>XNG5#N`ZW`9y7oBa z%B*@aAeAZGhLAXDFp0w0$x4faa-gPOj5uuWE8yLjS(G9YNM3k|NiNITaGuD=LJ^%Y za$zesEfXLHC%~#a<2PkR;$=oh!z+1l!8-3`^Br~Q%6l2|V1JaZ^|I?c zMf|%YI?jg64tW+VOBKrQg1$E#di$oTw`K{NGaa)hol(2CBIvH(`6&(_9K;P`=LMSE zFX@EUV=QSl>-s+fmGquxwA!T^D9bII!0tD5dZ3eaT!fls`>2u$g70urlXT2kUx(oU zfxg8i`{}Y=t$uzxi}r$<0XMkLebPeAF7XS#n*L0wLI~MsQ1|$seUdA#N9R&<=*_Z0Lw*G74? z+_b4%4>eF^j5&S%wpuN~Nie?W#sn${7a8HVl6U&ERIL9#h<{S)wi;b^W>+U%eMqDl zPiAVitjS6tE@^SGc{Y@{t@-n^W{O?|!GL~lKYS?10oRS(_Ub6(pfFVMWTM}RSQYuM* z$7oYZ@x3yZ0Z8fl15n?YL_I*^Cs88259$k6NHPJ4;Gb;y@927ppwBcPGztbZWRgJt z4=rB;%uRdB05h4SPN4XwVRSITS=NlOJGg@OuN(d)Yi?+0I$dRwLyd#)#B`eD3oH>T zMP_){;49Ep+sUU=`WTziic_ZKA+~BuSErE+T_qj4{^2XDR3q%Yirln-;S>{`a@Kdr z>$%UsQxyEz<;sBLW?SeOYd!5RZwYPU04EQaJ8Eb~kdP?T$NViB@5&rpdsu*@or^8~ Ii2tR(00QzRqyPW_ diff --git a/examples/starting-2d-car-racing/assets/StartingObstacle.png b/examples/starting-2d-car-racing/assets/StartingObstacle.png index 9be6e0446792c12081981b4deb92be199657f15f..4b3c516f7482a24f64ff6ec2f5c3c1190be97db0 100644 GIT binary patch delta 1773 zcmV4@s(>)9ZOyQRoI!tI8robY;)d3j(KpHsLI~p=( z;BOOv8{Fo<0f!1$^-BKPj|L171_Pk*>qdsD>^VMu{f?kvVXT5msqi}A%YT1x2z{n+ z3MiD<(+bROaeshpWTE{rIEP)RS%>|yjK~UT3(k2C$h3!p!5Ayq*R{Qz~EJDtru%hASBf5jBDi>2~X%Osg(;K zUgkpH7tR8T<;?_#^yFOtQs^L7XH*hQ4~-T8h@y&1_^wP3XTmsTg&furQ6Fk zMB&lo?JV?&RlA0(OVfEJC3vAtOv2=33MQvg5D5;Y-0J>onWj<-C?0K;vFQ@@$d~g^ zDEz5(`$R&8`3FxTS_G@)R^BZKwhC$xw&!g$Ycg$u1UWVTf_l9MVWE3dbB}M{YZIWb zkVBS)C4UGNfCJvPJIx8XzB-5N>vPAq{mi=`kW$D^re_|=TL*(;Zh1fNf7u6jjxT}$ z^5^rjf7(4hOeUvhVJ5X8Wfd4J=5zV7#w&;R`~5}ywuHTRWF{9tmHc$!}>nXL~xEI@&YO%MoAqkSBOCnV#H zG}!eHD`+c#dLKvqtmy7q?GoY&#Tr)aDzF`f%3FnUwmrVxSb(i%K+yYyRSo`vQDVsj zTz?%_&{hCrur0caOn;C;PCsZ^RjGDp1)HtE9R<)H9t8CMJwtb8ZYlsbFgM{I5XwRo zSZ}@%oCmJ-IB!7QsZ}qX#g{~2L~iihd3ERh-$jj^5!c7qPJ!LXhR7;3bOjo!u=mGJ zc!K`dGxP62m#{djDHsb16>y1h|2PTXf`8(ItBauENLj+wBwO^mOoCJNH?B~0D6|5` z&;XSoP-u8vplB-eK70%bki(#9(Dc0-lM&;NrhiQd zu5wdxYjZrAo}Pzshw=x0aA3y@xB0POgNfvfV?yJKswOd$B(MO^E0~+6A|!ukw{={Z zOFtGj;D8E%#G$ASX>d062*!;$?6|;;k)>7?+#@&qW`Y(|I2O!cLUJ6S5ub!l2IdHO zCI75sIP(LJ1MdS`OFVG&(0G-8^nZ=q|H_)K-<%+A_zCU-nl>q&HmAZ%dUX`wRA2_U z0oC%v9VUqQx-eQ*r){aw4fcc&XG-kDV^x(4+?;Atmz!=?R4O1aMK>lfE{HQ!JtBbt zwVRo*tpLiHVs7w_=+0zv8sZ97m&y1)%D6|Pf9rjxV+U9P#RT+4ab$W{s()G+dtWWx z{-8qvO)CIlk>Tvzqu7PVGm=G9PF?Y>-X}b3go;NmsYoINKEx?kK*CSYN(E^~Ze{;z z_lKTy8)pfst@B?dJDHcSM{Wrrgg1FLvF3HbQvnnn2e|?X|8U`3*ZhX72wLN(G4&-W zJZIKwr2e$4OLMs@QP(n3|n>af;6o25eT1ApH*9(`p zUErv{n_oTH@w*;aDqA?(C^6_QeNJ2J=I4(+mCBOv^ADdmYx-W{fK%SeK~~g8vRL|6 z86VekvGm3HC%6nzEKMe!nqP=mEX^yqm4oQT(k-G`0zs}o^@UVZ?0*LdK7;dl7!&tG zrsrFPIxvA_!VZoJ+Yo`~fr+Z)7Pf6Wi!AbQ6@~7{E;L26==0KtIzpcrqH0UGplVbH zrTj3R(SG%ZVwjtHJT#Tn8E*(HN{0(Sx-&7|e(y=+JD|W3xhtbeeuivH*2Pd;S z*!8Sid#A&2Xs%pw@n&|r?zb2EcyyK&(Z~$gA`Dv2_CxR%13Nqo3CQBZ7o!}uav`DL z)4pY_4^R8ySVp71M8#N+LYQ-eo@NletEoY`I-JA}4scATzWRAc+K>MMU-!km<(VW~ P00000NkvXXu0mjfj}SW* delta 1426 zcmV;D1#S9-4yy~0D}OE{001r{0eGc9b^rhcT}ebiRCwCuTTOElM-W{U>%vt&`-&VY zB*6&;5<>`qIPpq1bJh>=)#WQTwuEpXyTP_r<~P_?@dq{v7>!)Tku$2C&4~jAWsXd` zSF;$pwSAT;TyYzgFFYeyti}1zK2sDOj z_)*NRpM|aQ>)}`-x5#Sf?H~f4O)=VfdX1MJerYCTJ)GhF{_^d>_(O~blM3VW8Ch=cG)#Bd>14bI>*+7F zy91NlBwIHu{{d|HCpsGM#RK9hDEaOEj4H>!`}lip9e}`mf#86*unGn{ef??ac@N8f zcKu7O9e{TW;wqSGOLtuwOUmRdL!{N&c*H%(4S&eY5<5b#2@X$jnHkgG0p;xrK{CYA zqYkiCFJQuJhT!d`Isa09%+0ux-~jJP zBxHwZ^N=A55WWvq;KHG6@+0RIzIAV^3MWh$q6*{t5JuLd8SmlGCqJo^B2kKx?E7qD z9e;rQgkmeK8KNH7@qvY%%ZWAK!~NQ*%mFVAQK;k@H+zWVsR22fRM08AN?J$`}sKFAgQS@6%~K-^n-5@#Ov>j=GR{;XDA7R z`_f}jcLIcTMc)2Fc?_=kQ()Hji6a!`IJYA*(ZS!n%g)R zq;hPXKvMK#NK%xUN?IJVDg6nMAIY@1)2=!dDo^Q)xWsgJ0I@!Zm_A+^V&o>tHaF9sHCr$TPHN>5#@1(zE~xYz+k8wRfdX{ zSOgNT1M0Z}l|%-;J-cyM3*#{^k@dtZN0ZFKgISeU0G-g^i&9p___phahhvw?A=!wN zKv83FH9;e;fHHu7xDZ$;SYfVE(tn~VL^1zH?H=lVv6MEy`AuXEAqq0Qv5=g(4|{!y zrULn-3Jf`aI0ZSJm+2@XX$+X?kg!tO#o)#slgdKI7fm@{W%=l0>7Afr>GLs(rE~Ep z#)T6i$>kTRa(Q|>tB}jbWPICpu;Xo*xN_Jqu^eB+gcW(&D*|Idr6cYW@PF7YgbsMY zwuCFD)~jiX5yj9kIw7r1+Z<%C{p%lOZsTOR$DL$&Z7(^@)Te{gX*puL2d%SHVECZ6 zJ;YA^uzA{wsd>67PM=W-!umE(cRV#3`4p^-A)!Po9%-N77s3p$ef~fcX}$J&BxWY? g-AcW??eqTw2Lbx}XLV6F01E&B07*qoM6N<$f*Z8Bp#T5? diff --git a/examples/starting-2d-car-racing/assets/StartingWall.png b/examples/starting-2d-car-racing/assets/StartingWall.png index 1cb528294816932d086dca3c0744772e5b1a97de..a09e89c2daa2028c633a50c4b8b422bc84bcda54 100644 GIT binary patch delta 205 zcmdm`xtVE#vIPTMlDE4H!+#K5uy^@n1_lPs0*}aI1_nK45N51cYF`EvWH0gbb!C6W zF3P7SQd>58)#O*g8h*1qT^vIyZoR#=QSh(<2Wx(^;PCP4# z)wb)OYmXnQJo1PUXbb~`_VQk?bFVvA%_`b)rsv+b{|u|NR@q(7+hMLgIZ;Gwa)Ah& zQ3I>-AF=aiGTVPI{jTVc|NFz!JDgy>3=F}~xM!^9OMbFVdQ&MBb@0Cl5J ALI3~& literal 5306 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 diff --git a/examples/starting-2d-car-racing/starting-2d-car-racing.json b/examples/starting-2d-car-racing/starting-2d-car-racing.json index 4a1abfec4..5eed2f21c 100644 --- a/examples/starting-2d-car-racing/starting-2d-car-racing.json +++ b/examples/starting-2d-car-racing/starting-2d-car-racing.json @@ -1,7 +1,7 @@ { "firstLayout": "", "gdVersion": { - "build": 272, + "build": 274, "major": 5, "minor": 6, "revision": 0 @@ -221,7 +221,7 @@ "gridColor": 10401023, "gridAlpha": 0.8, "snap": false, - "zoomFactor": 0.40575189059439243, + "zoomFactor": 0.39251611754883226, "windowMask": false, "selectedLayer": "", "gameEditorMode": "instances-editor" @@ -906,6 +906,7 @@ { "assetStoreId": "112c69f62e03fa6df7716cc6b8f174c17857fbf71c8d3fffd16fa8fdbfa49bf1", "name": "PedalJoystick", + "persistentUuid": "0a237ecd-c8ba-4935-a93b-36827faf854f", "type": "SpriteMultitouchJoystick::SpriteMultitouchJoystick", "variant": "", "variables": [], @@ -1137,6 +1138,7 @@ "bold": false, "italic": false, "name": "LapText", + "persistentUuid": "b651f4fc-2a51-4d56-a463-96828080055d", "smoothed": true, "type": "TextObject::Text", "underlined": false, @@ -1158,7 +1160,7 @@ "isShadowEnabled": true, "italic": false, "outlineColor": "0;0;0", - "outlineThickness": 2, + "outlineThickness": 3, "shadowAngle": 90, "shadowBlurRadius": 2, "shadowColor": "0;0;0", @@ -1656,6 +1658,7 @@ "colorG": 176, "colorR": 74, "creationTime": 0, + "folded": true, "name": "Racing", "source": "", "type": "BuiltinCommonInstructions::Group", diff --git a/examples/starting-2d-driving/assets/Flat dark joystick border horizontal.png b/examples/starting-2d-driving/assets/Flat dark joystick border horizontal.png index 7c9c19aa888c541b07150210e894220b3175a7ae..4f772d873797453c44bb85fc6df481926391d4fa 100644 GIT binary patch literal 4013 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 diff --git a/examples/starting-2d-driving/assets/Flat dark joystick border vertical.png b/examples/starting-2d-driving/assets/Flat dark joystick border vertical.png index 722dcf9cff50a8c13c2e3b80870e8f7c7dafdf78..ec315d4b6b8b57c6528ac9b1f3995a45acf69ad5 100644 GIT binary patch literal 3944 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 literal 649 zcmeAS@N?(olHy`uVBq!ia0vp^2SAvE8Azrw%`pX1LIFM@u0ENCFP=Vq{^ZHa=g*%# zeE9Oki|r@bT7a@FB|(0{49Dy26;{rd0`fTvJR*x381$4un6YB1eHjA-6R)R>V@SoV zw|7tXH5&-H1pfN^f4|MPgN$nwv$b~Z?P$62Wn<%nPbW4n&Aor>UR~Yix7B<9+I*dT zI4z4=?)1i!y*rNn4R}9m<>^%UrR+C0o%1wsZ3urVGDT?t?{ASEY74g3a`*^e_$}A# z-pTY!CUnd5i5dy-@BAq+nK=9AFV(-_W-lnJ+2(z+J$=HGyr!F5WBHw4 zszt1hb=&@(NB4lt?+Y`Umw!vmdK>@QQtQrtCXT{}C$9xu|0Z>O`;`--I``Xh9)r)b zFMig2{`TRXGdyeTwGSP=#{RA_^~KWZWwquXgM8xO%KSU!YgjhxZ~9bkrazbWXMD@r zTz`~PVxe!0kk3x5y5utq!NL>f{|Q=puB7Pwo$p`#pSe~ZtA1OW(f?PcwQg;jt?N|1 zNn82?KJA~hgEdV;QjM!(#}u!kuSc#hb}NKVzu&_6T{D0sK*TjrkN@}^3;sI`XvQwIW&A6-ns>!N*JZK52%;UZV0>h~ zZ`QwQTR+WaJDq=9XMf?g)%mga>|4$I=l`#F$o>7#YTNtof0!d5cSdpUTeJ=25>Hn@ Jmvv4FO#oRk3(5ch diff --git a/examples/starting-2d-driving/assets/StartingObstacle.png b/examples/starting-2d-driving/assets/StartingObstacle.png index ccba3327451c2e99cae0b4f3e69ccee006154732..4b3c516f7482a24f64ff6ec2f5c3c1190be97db0 100644 GIT binary patch delta 1791 zcmV<6QHn=LzaXk2o-<>-nKi<3Aw&HhwJNe$G82=yC0BJ$W5ka z9>`k~MBfr(8J2v4JZ9EB$&tq% zzJJ|VfURXf(EEf{4gP{rV#x(u9ahj*0AsK%x{FMIkU>sAXjoOLc4!5gt-l=w&>kKH z^!`0VcV%uW05>o<;T{mmLKRqVz7U)TuJkx>K-{TSFP+7gL|{a2@Z5QI=lDl~Kj8mh4O$4z*G{?{|}?|(p-usEzK7z+v&aEWpMI0@f^;)1J-px{VZ z!qp^O^t()gQ}j2kP;@A?0>;n<1X&#F{c(n)@FFW(-FH8!ZiU~-j4QSvE(o0&x^MU@K*3<*-k>D>FX2#VcwL}qDu47o zd<+SY!=P!<^t~CA5#x@gO$n}YQ*mo^Jei)JhjEAU2Yzs1#|pRkv0sCU)0M09zo2DWpe`vRLT$xKh7B}F43V_6+s10dwHuVU`jXCVNz>JZlRutSLH~eOT z7E?GD%wR%t9H0@Ogii+M2zVv`tbb%U^8=0p?*m#(JaF{Tc$I$ijokmrny%lRAZ_>w z?g5%MDV;W_!b^H}6yQ`~2Dky$^28k`i1@lNT2-fQsn8Afgb!y*?89SKl?&XQYEze+ zZdFt&ATUKYCNVCEGgLhyfdRFfnXjz?%9vtq@QvusWO5qf3RRcM_&>_HM}MP#>wTwV z2Ur2c1oTC5WO`PrS{Hj?E#3a0Ljg@I0AZ2g?A)W+g~v0JMN>{)@vYt`JZprCM=z;J zA_G3eDOW(kPtQsPX+~~k|7rJ!o^u;#397C0UnV=5m#;@|2_b|xc{Q=-b-_~s6dnh; z0tx?c;ak`IhN}o#6Z99uB@^2M|?#C`P zMYHJh(uX=ipBbWROShnER0pN}FrCqU^@n1Zn|eGnmDL$<2rEj53qRyl^|jwrCnD0Z zIjSpmtwD#;&{MD9rg#qrC$l=(^{iWar^9e)u3T~PcDwGk7y5W~mK4#*4A>$JTF&-E z@D>9*JPirR;=>oC9JX>Hq2JTKWvmZR`{7tdqrODNSdK!NbA+B|5WTCZLAg4d#0?H` hOsBs3c}Uui{{dh3#l7X3BwGLg002ovPDHLkV1lUtKTrSw delta 490 zcmZqS`^h{(vYwfNfno0U#y3DpCBP@d)hDxX<{@7g@_{j+A`KhWdv;nCZ%sP~RL@xw z$1j`Slj9*yRC@0t+{Quk`)=gbyhZJXO^sB zIc8PP4=Pfb8|5-zEnFq{`d7Pa^4~2_P2O2Gw7)2QD|3OnXRXtciAPQc(+2m^zxd&>OtZ9+pZSH?=P^~@ae^Ly}!4``W z$lZxy-8q?;Kn_c~qpu?a!^VE@KZ&eBK3|DzL`iUdT1k0gQ7VI5W_oVoyp7Y6figcl zT^vIy7~kI6D9Y$4!g|48%RxzDfsv_(<5CuFg$St)+0OzFpLti9Z}fZdcHTYHcU9Xj z>v&zZ+0MoF?&A7m5~n}u?vMSaw0!@)!v%e(+P)W@Iv-m1xCUr40wl!cegAjKcHZ$7 zYf`4Qt@^X==gKRGKmWazdu{a!jpuQnHvgadN&2b&sfnNXU2A@lJ`mkqwgfboFyt=akR{00FX$VgLXD delta 181 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 delta 928 zcmV;R17G~79jgbB8Gi!+006uZmmdHC02@$DR7Ff_aWyqHHa0djH8nOiHZ3hJHa0dm zI5|R^?&H|1(F!EEFDZlRnwi_ z{vfi zj=HzjIAJ?^*aIR1yr3o5f0`Hd%5^<6F6Up zP~Fjl;*KVq#|bI9VKHMl8{r`)iPZ%{-xG$jf4=?q~^F0)IA24NSmi2{R2agHyOAVdeoQ zxPbQ(79!wkIWZ#NI!%xsUi&yP0Snv1t6i5;0fp`1zdpu#*d3k2|9q@mKxK#E_j(Kt z!LJ|`GN7)T^niore1Z;j$NmwypId1iKp=wpMU(OX1$5-Pn-+2DQ3 z3U8HfoorJ0VFQ$enSS6!7WRpkSLewMoFS+@5$ws+?STP?b}+=r0-O&JIbb}yTqpDq z_kD-lXD%rF#snxfL8d2)U`M4yN4i*I^UWcQjl?nfK%C*3a$s;&f%6R|JUAMx!11?+{;EmT?0T5AdBGkQj7xl&{aQ(}c)V~^X*e%3^Jziw;PzI z-Q)Pxrd*^ diff --git a/examples/starting-2d-platformer-shooter/assets/Flat dark joystick thumb.png b/examples/starting-2d-platformer-shooter/assets/Flat dark joystick thumb.png index acd005cc067032996264e510b58c8dbc0c102b62..8acf179f8660be36ccea1198105da35c07154fd7 100644 GIT binary patch literal 2939 zcmV->3xxEEP)IW485iPkW{Hp)}3kXR> zK~#9!?Oj=H8`l**_stBok)rm+alOTHY)jTcOO_Pdau+1&5)^3yq%HEtw|uhZ1*opyTSlNpA;d31v<-mnz&SAh32>in$=8J7LtSkrZm$^ucGWa^ z6!RueX69Q&R0kk4#epydaJxA@{SzY5+uL>0ye|c)so$qD^Rt?!y#%6%(#ihjpa33} zwtSU{|Jv1dLf$t5RM$Q7jHcj&qo=AHvH zcS_s(QP1g?WA~H*Teejd`1Ae0AQI2xJ^(YnIXQgmg^QO4CevPk+J^mmiNyN=@{&o4 zVR(>{nF#=xot=SYCOvlsnbnh>CyyRZO986tn{)y3TcPRC1JIJzWrHBEy*uVBgT{=5QcnwD$<2%zoR zxp}=+b&ZuN6`;C)U$gGXI0B%=iDW1=4MHq^%0dWen(kC?F4y&pqt$i$8dD-bZNvU= zX`1#vfc&(oSinvmE>F|7LxINTCma78=>)(5BZ0rEo-~mj}0bVfbBa1dD(v70D$6j@OmKx zgrIsyW}V4A}ur1`-h5}JruV9n#c1v5q;6+E@4)*S1PWL z%YrIv8+DJz(+Q$Vm%0RkxzDn!n%<5UJ8BOQLNtJ=!euPs-C!qx#;6vwqavW`y8af@ zZ_dtrA%SifuWhdgXiEy9XJl*u(H57hq~(G@me*Unv;ew(5P-*}Drqp7Xs{p(%Pf0~&CK7p|zqdWKpaih*^GR zUH~Dque+Q$3qjg9?+}0*TuL170z&KoAZS;hvEJ}xoN_5~9;RsqgsvOz&_k*&i**6BnP*W7-?<3WC5F?_yU*p`Lh_$VeO zLg4h8mF9dkLj-?b0rCn8p=lZ-;Yo}R55cmmc)nXgwGGX;i74unL$5CvW#tcmKDRi+ zG)-I`{20t?&62-i^Hz8~3rFx-Dcrhw9pTA|SQa!PD8_0=B0}jRvUAq%?SpLZ((6-%5XkYyTE9X@%+XK7 z6a5C6t{cf-8k(jB48t&tRdF{z6cG)v7C>8;1){Y#jz>hWZEIEBeJTKR)L)5&C!@~4 zkAx?&RuPFrmfWV4!sJ9KRzI=?12fTfky2vl=JiEIFwI#E58Yg|0^GiJ1Lh)8u9U*i z&1*5OnHfPHzz|}rb_`EWU}k0-Ma8Aa@%kX8L~v{rq2Sn>^#41{&8wF`MsBVjdHIFV z4Fi*t6Br*Ij`!IWA)T3TQykCnu`LU?Zd{wY+H>#S+$lmRIF3+oeA(PE^N;}Zjb)}E zm-R$(!+b>$QJYI#%1Wt0!N4__iIm}tur2eP%R_2lTh=84fI#B`cZ@j&`Maa_u$Y^c zqTD>-FqBfK=7%Dblpnd2I4;0D^8%12K7f0x%TfCwK_}(~7#|z!0bshkI48h#DBL?Q zz}1WWQ%b4BE-%hNO8LRS`L2n10RTV{{miu?XQE#S@$-4!Kl@`U93BKPOm0sw$is)d=)x_l)%__?2zYWe)< zcL{K|`;BCp8ZFJ5c^$f{XzIlF}*ihfT{o(A{=yRkKM}UB8{_Xzd9F$F@0f z4?#+aU~qiN?ZNReNCyO9=8nmU;6rCRT6{tZx4-!*1W$G`LoZ*uIxw90 z!Xk;N3_Q}H>)M+_h^l*XOh>6MY1=>S?YPI@DSy`x#_pyc=(_O&-6KzCXXZ zv-R)EFRdNIuDT|TB|m3)JU=C(O-R4xmdwl_o9666A+$gBo<8A7J?l6L+be1_gl??& zWMuqhu42kcCFuZWm{li0Pfmp=C(ieE&N_JlDUHI)+NLt0(OymHuaKY}b7e6(K)Ph+ z3(~e*zRiP*%l>rgtnHC$4;H4S$509AMTK!o)q_;4Vv8 zq-%bJcA5O|JulJAeuQX3P>IsnfaZ6Kar2%-@G*)QG=HZE&goRnsb>)#yPBG1sB!Hh z43d+OIs3N_GYkgUzhtn9^S>bspW_gq{VNwViuO^0PYl=T0VPPUzF*M(>6^wL0BHZ@ zvWDhTyoJGYF@JnE6Uejc!3TG#~e;_b+x__nEUko!5E+){gO+8MW_ zw!W>|D~;a);deGMLT(Z&UC$fvvaWY6#qkPCAl3O6NYLvOv|0Q7ArXJfHO(NbN$JSE zv_5Q9<57-dJ9nTwHg>n+?}Hn0XP~q-FhZf#^$lz_!DVVS2ZZ>r1OE$ zDnB>js#04BJOS+!)LgWQ)QhANRf`I(3n(Py&-rouzteNZUe|WK>$PJ7Qsy)UyaGa_Gl7Jb2{Kh10x)#!CYXPu7gdWgVBfow za5+3Z60O!||9_X?hPa{x6$YpC?G@TWAr?5cXK?<5!L#9CZeCmfhcsLPpI-SUs43cg zC>6GZOd;+{#yNdcisb^6C=bjJA%)F8MCT0@X2Y-Zr@M9l2Hh{M6W;b13{g)FFHssKloEfw9}8Rux`}4(^Ij7;tM9o`@YuFhOSWlb0V z##9A-oqvbG?el`G*(1K2@T>vpcmj6UqY&TNtlGY!!0=CBoxfMoKD;61*F2fMuoTtp z0)KZGG=NDm-JSaup78Z_f|1QaY<0cjbH+tp5fCnXK?SHPC{)Ia$^^%bGN}wEHwkb^G-G52O(mSf#EC1~d~u1-$(Iv2c`?Fs-0q0c7UMCVn4<4Y{bHHox$?w6^G~205X%C;y0hNk>LD zcj175DcDw1fHFnw?*yg5S*C_uO=ijJ=B+fvFrU5wTrGt*E;Pwafq+1!ruhR-+ zL+%!@BPAD;+S!2@3%`Pq$&|K_N?CsR8ypVyiAl6l0Is_b^)D1)1ezy0^cpZkiVn|54{?;Bpfww>TSWKqHGU%fZp@5F} z-Z`UaD%1*fvi1Bq)QXW2%Otl$WDs=wTA&tUtE*Lg4p|7*QP5XqD=R>!4`J&lybg82 zVIMdNpOt%cU}*#?pqf<+1!R+{2!GVUp<~CyTJ!aEqY|CxREkzQC@W8G1vxrr{b=Zh z?AsO!*m$~luiSe(g~CqP@#Apxvr|GDZAXsahNVgD_A0?JoVr?CsTigY4lI(6$DtP7 zK3`QO&~vJnO{FYqFcF4xOJE~0z5dtJ2yH5AosD9e{H?gY0bIT|Xlm^MPk;Mc;*MS) zTwcGp-1wc8fIB|1DWSuQ!Y6|xDCEb1%n$8c2_rQRy@rv zd3jHuG=h*)x{iM!@_^k9n}3rwJU*jaL4|YIMk06{>>s~9oY>r+z)LT#U!D*Zmr2t# zEFsDS&zfJDVk}VBw!Qok^N1d(1v2x)!Qg)$AB88y(4EGUvazfTSHhxys|x+&m=bgK z3@*~s6sn*xl%&BXcyvWO!B8VMLWPc=Ekbc=*sN|J-8b* z%-tZ@DS<)7=-@1z;Xa(-@+6e$+3AaW>=N_xDO7f7YjGJ2OqA#xU_Wdxui6Mt{TYR0 zxP}~v^pEGx15ls(-lv&}-Jzt3X$4en>1szMyj539PQvdjt=LtG;3@RG=9h*Xh!hDJ zfjZ%gjhH7wv6T%-Y=7>654XqO4rloit@*9RCGkHS=tQXG2>!JliP8H51$Tl9Q;z0e zo{*&GAxi;rwzR47wECbtWzL-m#v!q_1CByC=a*-1Tqr$rgac+^?7K4Q_nxSNVd$;Q?iqP6Ri3quvae zsNOdvK; z9D;ys9k2i9Cjafk|KHi2x&x@4rzFTPm_fzw_5I>AoU^>^w*=icxD`T4~i2Oe*{ z`G?!JM^OEkM(On#61p>gD{NKxJa6*G)e2`rWL%7|JI&fy{BqZeJb`78Yt}4iDKUN# z_AE~<(D$!UHIHJ&d&UF51-M$hRHyoR)O|ABBark>?5>m6sf7wXlYbmK+H+5kkEcbL{qYGWi_ZppzAeGxPo-|$%So$XisTJ*ZwmEl1O^v_r>mdK II;Vst062WKSO5S3 diff --git a/examples/starting-2d-platformer-shooter/assets/StartingGround.png b/examples/starting-2d-platformer-shooter/assets/StartingGround.png index 2a66b69a02b5edb6a6a4bcad3f4f10f259b5d5c2..d181229556ba7aa613300ce4062e7590851e4d06 100644 GIT binary patch delta 655 zcmbQoJco6HWIY=L1H-D!!h1l9v%n*=n1O*?7=#%aX3dcR3NjW4xjQkeJ16rJ$YDu$ z^mSxl*x1kgCy^D%=PPlIC<)F_D=AMbN@Z}%OwUc6w{coB0|S$|r;B4q1>@Tp(Rob) zBCh5gOi|e;Z{6#yX7(*EekowJ&~M@7j7$Cd>U)&*&5<1{o(Z>QE%L-egQopE$#&+sq1rw3 zw96XSX1&ZT{{;5_la=aKJLh@xm%H=?9buNqiiZ-W$Yz_x&REuI@oi(onU9~pwtWkg z&)Z$kep#cue6II@4dx@c`F|F=w74C1`ucSNUemladI{ZiqJ`-khT6_5S5x-Xru==LJ6zxKQn!i3G2er3tr zzWOzz&gpZ1_e~U2X}y_gxcK32EhR4XSFbFCN`6_juKH5nd-wRmyNb!{#LKwnH0;@S z``Zk~(5-dX7%QFK^7QAvTM)qQeQ@oU@0-#@D%smZVt<`#aCTm%ku`VTn#XTsgGygX z`|Iy3>kdPf^#^S1=*B%TqHW49CT25 z&LYuaAdsjq>7YYP;|VS{rEH0g7!DJLS%)21SQ8Zv0Yy2OC-U;R9B@$ij!UgG^8!nt fmc@G+_v^5ojCy3-y$YCs7&ts#{an^LB{Ts5>o^+4 delta 399 zcmV;A0dW4D1)c+t8Gi!+005o0f$RVP01Qw}R7HWZD4D`dlD$KTw>KgR0jK}~010qN zS#tmYE+YT{E+YYWr9XB600B5jL_t(|+U-|S7J@Jg3+?^yEGoj*CZkioc<|waX3a*) zx`^e+3pc?{kO<5HH$9r*9l;7eFPHfQM^6uX|I+*?;t@wj?|+|h#;5o0rYC69jJ-w^ z>~Q3Kx(JDb&lwmJegq)ix=SJNw1=Ro)>NBUb&GOBvRw?QlhMj#jA2t-57S7B`My1QOF|m0P#pN1``D~NLk{iNQ zo5wc?iF1OpqwItCJ_*D>4G+_ZLmlVU;~q6b5%FWW5caQZd7w6Y)pVg|>rA-Tt)aL! tmZ!#ZxZ!KochX!Z{Qk#s6a0hV0d}(x7s>EvasU7T00>D%PDHLkV1jh>v#$UE diff --git a/examples/starting-2d-platformer-shooter/assets/StartingPlayer.png b/examples/starting-2d-platformer-shooter/assets/StartingPlayer.png index a44a9fee247e11d8bf0301484e24c00a2a17c2db..4089b0b9b6adbf61a7c0d21bcf80125a1cb66f8a 100644 GIT binary patch literal 2406 zcmV-s37PhZP)IW4M0vjFU1*!l52=hrq zK~#9!?VHPQRQDCfKj(KJ^RO{CAVZ4U*a$nJG=(IKO6{Vs$Rgd4NLe@HAE?BxQa5ZW zb%)x2py{s4CaqM7MIXSTNmUg{N^!sm*r7@67=s5tW<0NZy7=8OcV_OLJNAr?iv8Qz z_s;eCo%8*j$M>A^6=GKP(xEfP(RWdE26T-7>m{>Da5bpQ@4O3(SKB^~UX(5kk7XS9 zx2l}sIb0%KE!S)3a~BG;Z2;(RhEE0RTm>2Cxn5c@Qa>X@Qrh2w-|3vU50(*Tmg{UrGsyY$Op~YU{|;Ss`|Do4(CvI-x3hR zIfKe+cI#3(ZGhbi0Gu}L^0f!R9sqj)>;bR`z#ag50PNo7ZXeCmrb)!kP}|M#*?$14 z6$t7eCTzLF*2~L)*(Qez0;rb54kzq%Gyvm(=kFi@#Z?$N1mlyKBS#^dgW?*&%rxBl z6vP4FZ$7D>Iy!+$1+)&wzl#~4g!}-kmk8#+f|+SpT7>*yvWWVx5A{cz;u^gCeaw&F zgndI$Ublui4)_`P{8NHI|H0l*Ih9R$TPBNn<1ILT0xD&w*DWxaEUYXOT)PZ+{%wKT zwj@|C!T1E`&3C}}V7XwGsa9aU1S1v#-rAID0g)$W8{F9nN}J--|Dz69ssu7Y=(P@(9;}J8+G3FtYhy0 z7z2+V+LJIT$eYsMq>K ziH(7J&B}am&+ZZ3GncoF`11R3|E|3ks9F{?uJc*1?KsFCS4Ho7J?%aY$-|P?-Zme1@thWM=vDcmles=r1LRF}&+nVpX zP!B?|bM|_(&5$&zl}Yw`Roig2EyuBl%MO0oC}IIl0J+VM;At?l0bo39{Q4S{%JGEF zbs(PwF9%dxo{?wtEo;oa#sD5vTL_?+0|_9gHULm-xB_3!!{=WKODj+;Lp^90?75K7 zz>$%F?~TL25NM@;q1_KG#D}$I_|Fu~FEzWs{sHCqQRV32Fb5bHR#)NgQ^NdXYp~~l zV>Fg$D>lq(HSk?{ZBlt@T+v!WSnQKCGs2B&p;Bv41GSKaA3317#)G8-{N=h( ztqFtK4X&BoWT1GCfCqsp|CoZclJMFIMHGT(Af)Aqe30lZpv!CEBrnx=>qj)xh=J!t|HI zP+o1zp&H+wi;~ZPJ2M9>B_)@M7^e!N7KFH@)(Cb9(K0ZQQ|`?{U4_?Q3UEEKAne4+ zdr+$>*QX4#^H!IJYEOS@DRbd{_v=L==YwO!+9le86v_Sl#?aHo-e3;CSb(*X;fE*H za+2!y8^2zJ4{sY5SF8>jXCw`KBqoa~sNj|=Hmf%wvvI<7Xl_EmO&cgfSy(N>-);)8 zPAV^qVv}RNk0GIUzA7%vEx^rbp;m{XY(ra#=q~X#ge|!%+Mwq8v^xkkp$V-Z30YsM z2k@^u!m%fCVq7tP|GdHYcJe;;nQ(v3@{+6{@yaHB5)RNP1mHSxT+rEc@o59Yaja*} z%)_H);gxY^WW;{F)!blQyVcSX+?o-Vi;&Ai=PR0^Q^In8mT$F}4R<~otD?rO2?IIRgU&W+MeJ!NJRjtCYC zu(&LY4#Bram3;%?_+iKeF^#%`dIeV2;J*vP!g4qd&1}U(WAYb@YZ^w2c8^>m!Dtt_ zY~&Onm$4r_T7`ut!u|o+msj@XA>)DXScX|w>oqGySSbom)@(>+8p9ubT7H{=OF9$L z80WC}TstJucF&P+lVBGQI?aN!MhQG9RiL;oEEMAP*T9BAF59p*#yg!Y=oqolbtMih z*`jN81CDMuMk_~8+u5X*=g`dRPbVlqf5b7)th&_YvNsde0`M%wePWVgtV-A}?QU}%qFjTjimG$bWnG|IPI zj8WbC*lgo(rTMz4cZq4%8dOnQPD`6YK$_jOs~KNh=(H#)EvF<&J|7obErS_pc}65g z$EZ$`{Upv#!8JzH4N4q{B)DluG9?KiCS4>QNuj$fU}>CQ%^%Gu6 zMGKMW9x_i Y0~3&?=l|E%i2wiq07*qoM6N<$f+FjT2><{9 delta 452 zcmV;#0XzQY64L{a8Gi!+005o0f$RVP04h*SR7LQO0P>Fkqhki$g8@uwapZ>qonQx< zUI?jY2Jw#puW1INV+a4sTluXnXy6|T00006bW%=JfTDo@04DnXvZDY1010qNS#tmY zE+YT{E+YYWr9XB600BKoL_t(|+U=Lyu7e;DMF$52LH_@D%YUU-DZ5N`R$1n>wR!6Uf4z*^hN z97+v=T|Jgiste*E$9btP*alVRke8~0&r?c6QTkS}k_79|1++Ez&$mF?2liwu+vZNp zO6|jB+Xu+r@qaiY>J9SG4RpK(sAYm7DPl&W7vL<95DaMI(Af!!yc1LNQaXVt@^V%u z`TdyyldREQ2uub}4MES4=G%hiQlgSXL46YWlJ^8|5?FovnP6@rr8+23mt(quz_z!s z7N-at${i8YsF4Ic{z71exQ9S_;`Pm;ll5HJ?03B&xjnUg(sth4pBLIeFv86QI($Pz uf39_OBG5xjAjSj&_|Oz4|KE9N0{jIqW{22VyrJR%0000}Zaxm$UA`u1!#dTDQP!1*)$`FL#Ie)91lpRxyg&OLDAE1x~%Ucl`I zVZdXre8X#f=3kS%yT>R0{Ky<-XCC7a(@2AKru$|5Nwfcf~!+?O7w>n|HqcwtntE55c!U+=N+=oexv?6P|Ve4F`q*?`BZ9z$VOj z$u(+VOlTn(gOAAJg5yGTBFn@R@1^@W_moH)6~G2~-*;^s7y^>+E4f z+F;+JNN;;KD)EdNaOSQzydCwtmr3p+p1SDh? ziVX^46Mbk>8=EYI*znvua0bz}A|6J>6fuG+4?ZiwNGa)ay^J7|cd`K^?81oSHK_n7 zS$A#A-fcSjJ8toucij364dgRofD!Nfhaq+kw88)egu{$|h-4qe==VhSK&rs{eGmiE z2LVNq%|Sy2&@15Hq%4aLqKmebK3v^0=!X$JFSJ$6qi;&|zYNjK`VW}tpq12H2LpgT zjA)TM7+QB73O3ue6%#gj*A|F**U9@o|93x#wjamm3kCZ~ zeIMqGbJPLv2AhygjCgDT-rXE}7J_?fnk2&YOc{DHz!in7W3)Mxj7ob*uK|H?o8TfW z^L3d8nBc;&t=kbZUc+{-VH02il6QVnC;%m!AOgbOW{S-gYO>95Z#wza@2?KumjN<( zgWTWmHSrL3VS*U23j?s+**@^LJ&+vB7&6gu6S67vfrPwHb=`{SJj!oT*^a9l{K(>h*;z7Az1JjcXoGcM=I4MxzIA1r|%sgA70rfypYM&2aCn)e6T)M$I z#T8p^N?R~ODA?9OCAMMG5ty;(^4XmyZ?Ht(&=2s<;JvsdJIwBq^jt(b(|L%`vMbnb zF#*E;6}(pkFyo%jvG!=DgV3U64?9Y1S)9j$r99}l&Sw}s^ghe>I`}g@ObF>p7V;~>W&1^zRh5>uLNk#aXL>1% zCB99Q@pr5Nq>p8jw;7D7U7LEEHDUq;v2OHtma=A%THnJ33{ls$1->4OCR*gG+W3J< zX`wFE7Cxm8$U0;HSWaOfdge3RGZTKh>)p5B{?!jY{>YhUh5+1K9>uzj#9bKkB6QsC z^T53Xv4pfp-tDn|V+f1=ELJs>i>U|H!C&Emq%XrC@rP3>7eBDVL12=X535R!;|gtU z>3dq}?5}|@LmTJPec6O2SN!B%4jl~~e9-jiCSz zCH@5POCRBumpz@-@7W%GpV^Q_p5&Ah2( z)e#Xe7Su8C*np_N3(&9d7ZQm8Q;VtMTpSGBKKuTFV<*?AZsL625=pV*ch{C#-soJJ zs@Bj{`F2!#I~Hn?TpN7`9}p1e#BZ=w3F5~vR!cITY0@6=RDMc0_839i4`cwuzQ@b6 z!?6KG?49fs1cwQM${6Mf4SP9=h)vt1CE-SV#biN%2uF%dDZ=H%LI^=Z+sg_g3TLRD z_PDSvn|{u~J=VGDY~J?sy3w^_NZDuUXOynr7G=L($bS%ndDkHmcuJmk1{h@S3%MBv z8G%hKK!Xt$r-&vZRvGsjGT_m>Bz=DzRXUJgueIDpaCmugbLH%ayTv3{P1O;fAiRcf zNM~|IDXCV6((VsNMwE!0_*vU8I9}9EuN`6(z`6!Z5Ni4(-%jkl(DsKgjAZ}rxQ85l}HO^#XRIWtC-TA&_jUbSVB;}Z5dL04NC2^tL|(=7SdJdFW}N_{$@#B-cyM|?!QEDP(83p@zFhf$zMH8QwO_Tr~L_v70>nNff4ozfS z>`l{$zHUG`n#$;J81C6P{a2HAE*2EenyI#}cK}Tr?*c zMV~`_5$3oEs%xhLu_gO;+Cu1T4_a1sMyWuP(%S z6wV-+Co_W5dy_uHBoFa>40*+ptTDJmbH=@Np+enZMC$j`&XFS!<6g2;|99NktmBc* z)?9bA(2vy958UQuY=uF@nmgIA_u4jTN#V7(;Wpw;9lAF(gUV$CJt&?|G zS7{)NzW@%Be6!N`X}S!oFX|o$7x68`Ke4-;PV7`sL(kK`KGVjt10rjN&~b5znskik z%s>*x(ajfWu&B~Z=Xj^yU22)kHJ8TbmNQ4qS3=7fSulY@muO4N)I1Ui`D?D)03Yt) zWaGMwtvJ`<0J9PKb%=Kulop>}lQ^S-msW zGK51jh$~B8%Qs`LAvKnQV?8y(m5~;GCTJ`d!rnM!02!PObf2tLQo$nnRsJ40Cbt}9 zata?Tv`x$KR!MAoF_Msex_O?~i+T6kw!8-j==IY%!ThXqFNVlRzV)pr!XJL}$>bj0 z?yee^)vHCKjV_Uy>czB?yz?wUhmZY!! zQ5n4xl0LA11YsljpSkBA7;{wTI~92N;Up&9zIKZ-fvaH}s?g_NjKQ{^wI$}x(@HvyK9L4n0M|bYUa2{j9IF29D?PEOV4j*iF+Er>LE%SsbS6KCm zfE`;>bxG`UnaJ!NqJQRwvx81r{aOL3yHZvDh+#{&K!@IlQT{z`| zy?1YUOZR?`zy8vf9_A;XfbbJ1PPpUz;}NC;ekA{lXt$~T!SulFfG6}+rF0?6^VaS! zmMTj2ihK%bB^fhoWRDvT(D+_vkLK`Cs%Ok*!% zm&5Z4cgoTqG^{q0t1{XhE$qD60G5C!rrSP0KK7>!a%W+}g>JBYeS16fVa!VU?ujSt z85ibf{6#yu3|HB0Y?z(t-pl)9M|5v#ooli$mS%_e#^&9X+(eIHZ2PV?L6zvo4;g@E z+*iRC5OA^6jdl~9Ae)C`=ppqrnOk10%q`()aQ)62`(Ej%eR4X3+Hc8X?JBHV_=`aJ z1QHF~Q=d<#{*eipwq7qdN&N1+?fpjlsZ;K>h}WcM+y2~)IA(p_?-(;j-VX;<7i&L- z(Rvq9^yY{uaL(frJ7>j5XTxARc%(FpyX(#1`H>`IyXKm6u8hZCDQt!>RKdwrC{^*o)Bt-7??R_q5PKOE`1QHJa0XkjK^+4WIMm_x zwLJ7+mfLktJeh~HWobjiOZO#xySEy4sNk6%gt=bhXXD1t+p)cy&^=f|MKjZ0bw-@Q z5L<qm=j9(qYT2}I|XpxPZx7KjeTfrVxu!bBIgs6v#Lu)+- z5+-VIw56``0yM-24-wFY;!851K6NcA-Y)7!Qk~wVUaYI0Q>6x5u|fd}kxNK^@#nEL7qF@vy9>ml7a?wzlkv6qfQZm?fl6H~A)>^lQdi{v zloW{s?Z#lK;&M}jX9DYWp5l%cy72@zc@4;a*O;Z>w_)5~MHDV(ityBCqdCN*zov51 zZI0`OR<8w$&{G{Dr%E)aQ?r9!AA=b?WKNGV!=t!p$MEfsfr_Ib{JST={MkDX5zraJ z^cm~MQr#*=MImH1QXilV@B{`_IfpDB16vu;h1v{b9SjA&%odVGwFq-fmWj@^?^n5y zkZrUXC5j-49+5>Kb%@wmjEv}AEcWi00u%@kVphca0SL&{Xfq2V?8}_z4}>xc)_Gk` z&5-rqquOvfYw-LN31!q`fDM4dYOJ!}qCQ>`1|+HLM85j{E4r_y#G?C>+EJW(a+2dA z+(>ke!5H9G4T8F}+Gc>N6{^^l5`fCsf}eEnAllO8nPjj@&%;`Sfk1LNB}};TTflj5 z!pY^hQcz+jjs{~+-ND^g=l%Y4Y=$(%VKz%+hH`PdFdK!cPeCoLpx-BE4V|CD#HcTq zP3;Ie$R4a3DkMPBLWUCJ)nWra-G`pW0M*gC;A>pWMb?nix3x!1i0zPk2etWZjf}@g zD4-_7G#Z-#`WgDN#w;^|34+&bWU!5yp<&Fj3S>!#P)eM3#12*jcajdbYzLhSY0a(` zb&?nHLCy@&pYqIJpLb>| zrPrt@YfWptLqj-X%~YDdP#i^*nBmgB*HZjc1{D2Iz%>Q-BjX@hZEk{i_2jWIrEzW3G%MdM5poxhIS{e4IbuKiRoznD)ahM*x? z$%+8fEI#k4vX1y3D-b_q$~@wevoO{D+=X?#)irgHXn@!?%^m1Vctfw=!sO0>Z*%2S z7;y5wf4BnoK449VWb3UHD;WdUtQm>SJxp0AL9jujyDqD%41TXHU_@czsF2XdWDFpn znnlz9ic(GoVY${YA;l%k*$?`MD48EjJBAVFvd*jCl?|vPS0to+MXP3;Om28xEsXJM zoM_cm(?WDiYw8t>YL{W2kh5m3bj%;m(0U6&?g!lWY}f4&IsNK4eiI090Ae*qTq5;# zS;y1|iAtQjlwEQJSFqWaor=l5t+=#o7tGnHHFyo_FBU8QYK7Hkt{Zc%(tPY_ASNUl8{feB<+ znj^^P79Gd79iP1ab5AReU$;L15`LGp<&$u5k(3z4pHsUlMaWi8uWGVd1DKMwqMtQOavx?;UWrOwmpld3x{}3p(H0)WC#R$)jF3x#H(DmeW?j8P>hWqbp^!SmzrmBJ9j0U`U&`(EUwB`nM;){JD=_kJcOZ2&KIDepfHvL@>D#)tiK8q5Mj) zCgl>(}xRo$Ax{??&&&6g^@ET;h&s?yj> zJdY76Tm2Rq(qIb2uqU}zpmx!q-+qHxY#<1AhJl23Q;kwcL`>L7>LGNA^XXE`XYaoe zo!_@j?kpAKDx|L{2I_hQ!*1wW<@T(9h&@WapIZ}^zVvXDJj<D?3 zl`8%f&D8{}5*ZfF6LPP52L*LXUgm5qs+J{%wY*Jh6dwiY4Kn{5Uc@>hx>E*AhK_nnJ7MVh*6lVv$hjV%6>2`OFE zC>ghGwCSGb)hM~ztaHTqzgYGK8u3}zFF3Y7!Qj+`Gv?(JG78k_!_>;@l&Nv!=CP$f z*(34XEL&w&W4NYk)cOFIOfVNWg`5-!etYbN;1N9X6?%RO{$@2xMAYjf^ zqOw8@Sx2i0uyEi(D2LaEu30+hixKr-P#J}7Z9^?RuvW&UTaI619pkpvcb(#0v$U+g zh~K21{Lb5-0rAg@0oqI*qWhSWWhX_8U!W%Gu^7OE)Q+N7`HlgJP(Ul7Q?1@g?D}4) z4pyD9kEc5QSGm7SSjtM`+$a~F%2h7HDTwjD&jo!#UFFpV=IGe>0$sa&hXQqt^DvgX z7o=T&waGBfgZn3R{)nK@+D2|Fo2awQ$<0qutu(W)J3}2AU8UCBCo@++n9ypXcBjLw z`&7m{Je9`smcdcSjjG{MA5JM07Xk(#gkZ<5cb+O8lZf;?#XNG`EH%aytlmf%mY$TO!A*s`;)pPi7Rzhiys(Ucrt`+QCf3OKp?Zm_FAb{|c>;>eM|k%OmHyrr@&gG;(e={cpaRv%5J zjtSkz6G?vld7k&YKAO5IpUs(pOkSVO9kZ{-tl3KNS9b|6k1}{_ya>)374+GheKp3t z`fTow&8<&K`c{Yu3V1MrrUpq_X)!<{x@D=%CJ<5ZzCXM5E1&tWd!E}(`*2d5F~a%Z zW1?!eTdaC=x;7O{**D%=pF8(j40v#V|L0a8PA03|EI*u-8UTH-Nnb65C&YkJq9yy`Cg%p3=G%Hoj!61{v~c%pa1q^HvCbl z(;TWkIR-)hPPjSFQ})HSn11PIN&5e{8*Kjr^&6{U?oF0s00000NkvXXu0mjf5kvCA delta 578 zcmaEBbAe@oWIZzj1H;_yjcDd4a<^vBt@!)m>b7+m`j*m@KL<&~mPLHsV*Rv(L7<28OQYfP<>s$M9!Auj zTf>vj!2N8d;JUZg(({up-eh>;onl<0EY?^5*SVHGfh9W7)T#K6Kz+jEjSnX(%LLZ0 zvA?umX^z*D;Fc(q}~lDk2W(ER}@V@X5J`b_xj-V zNAb)BdVh{JvaVxH_*rnsFYVZBhB;?L@}{j450E|Jx1ydoXa^t1${7!&MDH+c;Cwf= z$?^_kOQkQ%Rfjffh7;MkFT8l`=Q0=QX5Fg`(uVUbKEzCOlfoH;5lK>&6(^Yl)PgLvveyp8x$zYG%xrkpr?Fld6 z>Fj6Vh!tQDoa0ug*y$ZvGxNyx$U6Z~bbarH9L+gmD diff --git a/examples/starting-2d-platformer-shooter/assets/Target round button.png b/examples/starting-2d-platformer-shooter/assets/Target round button.png index b64abc31b967aa6ba32dbadeb28994865aef26a1..e615798805c976c2a97557422b6358f983d6e6e3 100644 GIT binary patch delta 4367 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 n&BnTFC??`J7Dfw3*!Bl|q5$ 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 FV1j1w)3N{n delta 627 zcmV-(0*w8$9DxOp8Gi!+006nq0-pc?05(ueR7Ff_aWyqHHa0dmI5;&mH8L_XHa0dg zGBP$cHa0dk|NsAAUtjX_^4{Lw#l^)&Mn->se~*ukx&LcZ0000AbW%=J0B>IZjxp~R zy4HO*rZ@lq010qNS#tmY3labT3lag+-G2N400G-cL_t(|+JD8F>ypAC4227X5~x;v z|ChDy&bnGlZe(`P@6Ii$Vi%H32h?z{` zA&({os!ck)jd&rN1@*kv&X;~w(7+>zIGK0^u?M`ITYobVd%(4ET6Mt2>z{${OJ<6^ zsnagq<8`6Sd|rA-EH^URbgQvHQ^O|E%x{@$LOs$9eVLZ5Rh; z*XQ!VgKQZc-=f2o!?Ed*2lmdfbz>*?te|XMb>FPA<&m<}EJWI#E9%{YoXZKS(#-Mi zEz^go>*bgJ1}U?hv%<;dkt!VPW>+x%4v0{XX>l^EM0ovUQ4t~&+a0mURTHt&FNy#F N002ovPDHLkV1nr&BUJzZ diff --git a/examples/starting-2d-platformer-shooter/assets/starting_bulletfire.wav b/examples/starting-2d-platformer-shooter/assets/starting_bulletfire.wav new file mode 100644 index 0000000000000000000000000000000000000000..f7045dd18c0e6f5125fccbccfc36f5023656a915 GIT binary patch literal 6148 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 edhy=ZUaI$;!{ literal 0 HcmV?d00001 diff --git a/examples/starting-2d-platformer-shooter/starting-2d-platformer-shooter.json b/examples/starting-2d-platformer-shooter/starting-2d-platformer-shooter.json index 139aec3da..fd0c322b3 100644 --- a/examples/starting-2d-platformer-shooter/starting-2d-platformer-shooter.json +++ b/examples/starting-2d-platformer-shooter/starting-2d-platformer-shooter.json @@ -1,7 +1,7 @@ { "firstLayout": "", "gdVersion": { - "build": 272, + "build": 274, "major": 5, "minor": 6, "revision": 0 @@ -178,6 +178,26 @@ "name": "assets\\StartingTarget.png", "smoothed": true, "userAdded": false + }, + { + "file": "assets/Collision.wav", + "kind": "audio", + "metadata": "", + "name": "assets/Collision.wav", + "preloadAsMusic": false, + "preloadAsSound": true, + "preloadInCache": false, + "userAdded": false + }, + { + "file": "assets/starting_bulletfire.wav", + "kind": "audio", + "metadata": "", + "name": "assets/starting_bulletfire.wav", + "preloadAsMusic": false, + "preloadAsSound": false, + "preloadInCache": false, + "userAdded": false } ] }, @@ -210,7 +230,7 @@ "gridColor": 10401023, "gridAlpha": 0.8, "snap": false, - "zoomFactor": 0.6040838749988916, + "zoomFactor": 0.6244537826600048, "windowMask": false, "selectedLayer": "", "gameEditorMode": "instances-editor" @@ -364,8 +384,8 @@ "name": "Target", "persistentUuid": "817fd0d1-0cd9-4250-a147-0807d1148e6c", "width": 0, - "x": 1120, - "y": 352, + "x": 1152, + "y": 384, "zOrder": 7, "numberProperties": [], "stringProperties": [], @@ -380,8 +400,8 @@ "name": "Target", "persistentUuid": "027f7422-9eec-4e5d-9496-62bf978f04ee", "width": 0, - "x": 96, - "y": 288, + "x": 128, + "y": 320, "zOrder": 7, "numberProperties": [], "stringProperties": [], @@ -396,8 +416,8 @@ "name": "Target", "persistentUuid": "c549220c-fbb8-4631-96b9-6bf17e63325c", "width": 0, - "x": 256, - "y": 480, + "x": 288, + "y": 512, "zOrder": 7, "numberProperties": [], "stringProperties": [], @@ -606,6 +626,7 @@ "adaptCollisionMaskAutomatically": true, "assetStoreId": "9c727020616afdd6ba786b8af206a90481f07db0ca175ed6a4cc5b7e01c66d06", "name": "JumpButton", + "persistentUuid": "d90e93d6-5f28-4370-8935-cb44c9cc6adf", "type": "Sprite", "updateIfNotVisible": false, "variables": [], @@ -628,12 +649,12 @@ "ButtonFSM": "ButtonFSM", "Tween": "Tween", "IdleScale": 1.2, - "FocusedScale": 1, - "FadeInDuration": 0.05, - "FadeOutDuration": 0.1, + "FocusedScale": 1.1, + "FadeInDuration": 0.02, + "FadeOutDuration": 0.05, "PressedScale": 1, - "FadeInEasing": "easeInOutQuad", - "FadeOutEasing": "easeInOutQuad", + "FadeInEasing": "easeOutQuad", + "FadeOutEasing": "easeInQuad", "PreviousState": "Idle" }, { @@ -704,6 +725,7 @@ { "assetStoreId": "e71bd69f896d6c7531b48c65ceb5da25071d4fbdeb518aeceecba8d21f34ed8d", "name": "MovementJoystick", + "persistentUuid": "e62f9a89-38da-4136-b68f-3b1c31dc2c6f", "type": "SpriteMultitouchJoystick::SpriteMultitouchJoystick", "variant": "", "variables": [], @@ -806,6 +828,7 @@ "adaptCollisionMaskAutomatically": true, "assetStoreId": "523c449a51200d76f5572c755155325f5583aa5a0229949273647f2ac9db8c34", "name": "ShootButton", + "persistentUuid": "1a69ff72-be3c-4e80-8823-3dd508fbe29a", "type": "Sprite", "updateIfNotVisible": false, "variables": [], @@ -828,12 +851,12 @@ "ButtonFSM": "ButtonFSM", "Tween": "Tween", "IdleScale": 1.2, - "FocusedScale": 1, - "FadeInDuration": 0.1, - "FadeOutDuration": 0.2, + "FocusedScale": 1.1, + "FadeInDuration": 0.02, + "FadeOutDuration": 0.05, "PressedScale": 1, - "FadeInEasing": "easeInOutQuad", - "FadeOutEasing": "easeInOutQuad", + "FadeInEasing": "easeOutQuad", + "FadeOutEasing": "easeInQuad", "PreviousState": "Idle" }, { @@ -893,14 +916,24 @@ ] }, { - "adaptCollisionMaskAutomatically": true, + "adaptCollisionMaskAutomatically": false, "assetStoreId": "", "name": "Target", + "persistentUuid": "ee2140bd-bd44-48e3-9b2c-2a5562076dd7", "type": "Sprite", "updateIfNotVisible": false, "variables": [], "effects": [], - "behaviors": [], + "behaviors": [ + { + "name": "ShakeObject_PositionAngle", + "type": "ShakeObject::ShakeObject_PositionAngle" + }, + { + "name": "Tween", + "type": "Tween::TweenBehavior" + } + ], "animations": [ { "name": "", @@ -916,8 +949,8 @@ "points": [], "originPoint": { "name": "origine", - "x": 0, - "y": 0 + "x": 32, + "y": 32 }, "centerPoint": { "automatic": true, @@ -928,20 +961,20 @@ "customCollisionMask": [ [ { - "x": 0, - "y": 0 + "x": 2, + "y": 2 }, { - "x": 64, - "y": 0 + "x": 62, + "y": 2 }, { - "x": 64, - "y": 64 + "x": 62, + "y": 62 }, { - "x": 0, - "y": 64 + "x": 2, + "y": 62 } ] ] @@ -953,9 +986,10 @@ ] }, { - "adaptCollisionMaskAutomatically": true, + "adaptCollisionMaskAutomatically": false, "assetStoreId": "", "name": "Bullet", + "persistentUuid": "2b02874b-f47a-4ebe-ae8b-174a5fc4a091", "type": "Sprite", "updateIfNotVisible": false, "variables": [], @@ -965,6 +999,11 @@ "name": "DestroyOutside", "type": "DestroyOutsideBehavior::DestroyOutside", "extraBorder": 300 + }, + { + "name": "ShakeObject_PositionAngleScale", + "type": "ShakeObject::ShakeObject_PositionAngleScale", + "Scale": "Scale" } ], "animations": [ @@ -994,20 +1033,20 @@ "customCollisionMask": [ [ { - "x": 1, - "y": 5 + "x": 0, + "y": 17 }, { - "x": 32, - "y": 5 + "x": 54, + "y": 17 }, { - "x": 32, - "y": 27 + "x": 54, + "y": 47 }, { - "x": 1, - "y": 27 + "x": 0, + "y": 47 } ] ] @@ -1088,7 +1127,7 @@ }, "parameters": [ "Platformer_Character", - "\"LShift\"" + "\"x\"" ] } ] @@ -1121,7 +1160,24 @@ "Player.CenterY()", "Bullet", "180", - "500", + "600", + "" + ] + }, + { + "type": { + "value": "ShakeObject::ShakeObject_PositionAngleScale::ShakeObject_PositionAngleScale" + }, + "parameters": [ + "Bullet", + "ShakeObject_PositionAngleScale", + "0.05", + "0", + "0", + "0", + "75", + "0.15", + "", "" ] } @@ -1153,10 +1209,56 @@ "Player.CenterY()", "Bullet", "0", - "500", + "600", + "" + ] + }, + { + "type": { + "value": "ShakeObject::ShakeObject_PositionAngleScale::ShakeObject_PositionAngleScale" + }, + "parameters": [ + "Bullet", + "ShakeObject_PositionAngleScale", + "0.05", + "0", + "0", + "0", + "75", + "0.15", + "", + "" + ] + } + ] + }, + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [ + { + "type": { + "value": "FireBullet::FireBullet::HasJustFired" + }, + "parameters": [ + "Player", + "FireBullet", "" ] } + ], + "actions": [ + { + "type": { + "value": "PlaySound" + }, + "parameters": [ + "", + "assets/starting_bulletfire.wav", + "", + "30", + "RandomWithStep(0.9,1.2,0.05)" + ] + } ] } ] @@ -1183,18 +1285,66 @@ "value": "Delete" }, "parameters": [ - "Target", + "Bullet", "" ] }, { "type": { - "value": "Delete" + "value": "ShakeObject::ShakeObject_PositionAngle::ShakeObject_PositionAngle" }, "parameters": [ - "Bullet", + "Target", + "ShakeObject_PositionAngle", + "0", + "RandomWithStep(-10,10,20)", + "0", + "RandomWithStep(-10,10,20)", + "0.05", + "yes", "" ] + }, + { + "type": { + "value": "Tween::TweenBehavior::AddObjectScaleTween3" + }, + "parameters": [ + "Target", + "Tween", + "\"DestroyTween\"", + "1.1", + "\"easeInQuad\"", + "0.05", + "no", + "yes" + ] + }, + { + "type": { + "value": "Tween::TweenBehavior::AddObjectOpacityTween2" + }, + "parameters": [ + "Target", + "Tween", + "\"DestroyOpacity\"", + "0", + "\"easeOutQuad\"", + "0.1", + "yes" + ] + }, + { + "type": { + "value": "PlaySound" + }, + "parameters": [ + "", + "assets/Collision.wav", + "", + "50", + "RandomWithStep(0.9,1.1,0.01)" + ] } ] } @@ -1323,6 +1473,14 @@ "name": "Scale", "type": "ScalableCapability::ScalableBehavior" }, + { + "name": "ShakeObject_PositionAngle", + "type": "ShakeObject::ShakeObject_PositionAngle" + }, + { + "name": "ShakeObject_PositionAngleScale", + "type": "ShakeObject::ShakeObject_PositionAngleScale" + }, { "name": "SmoothCamera", "type": "SmoothCamera::SmoothCamera" @@ -29672,6 +29830,4479 @@ } ], "eventsBasedObjects": [] + }, + { + "author": "Tristan Rhodes (https://victrisgames.itch.io/)", + "category": "Visual effect", + "dimension": "2D", + "extensionNamespace": "", + "fullName": "Shake object", + "gdevelopVersion": ">=5.5.222", + "helpPath": "", + "iconUrl": "data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iVVRGLTgiPz48IURPQ1RZUEUgc3ZnIFBVQkxJQyAiLS8vVzNDLy9EVEQgU1ZHIDEuMS8vRU4iICJodHRwOi8vd3d3LnczLm9yZy9HcmFwaGljcy9TVkcvMS4xL0RURC9zdmcxMS5kdGQiPjxzdmcgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIiB4bWxuczp4bGluaz0iaHR0cDovL3d3dy53My5vcmcvMTk5OS94bGluayIgdmVyc2lvbj0iMS4xIiBpZD0ibWRpLWFycm93LWFsbCIgd2lkdGg9IjI0IiBoZWlnaHQ9IjI0IiB2aWV3Qm94PSIwIDAgMjQgMjQiPjxwYXRoIGQ9Ik0xMywxMUgxOEwxNi41LDkuNUwxNy45Miw4LjA4TDIxLjg0LDEyTDE3LjkyLDE1LjkyTDE2LjUsMTQuNUwxOCwxM0gxM1YxOEwxNC41LDE2LjVMMTUuOTIsMTcuOTJMMTIsMjEuODRMOC4wOCwxNy45Mkw5LjUsMTYuNUwxMSwxOFYxM0g2TDcuNSwxNC41TDYuMDgsMTUuOTJMMi4xNiwxMkw2LjA4LDguMDhMNy41LDkuNUw2LDExSDExVjZMOS41LDcuNUw4LjA4LDYuMDhMMTIsMi4xNkwxNS45Miw2LjA4TDE0LjUsNy41TDEzLDZWMTFaIiAvPjwvc3ZnPg==", + "name": "ShakeObject", + "previewIconUrl": "https://resources.gdevelop-app.com/assets/Icons/arrow-all.svg", + "shortDescription": "Shake objects with configurable translation, rotation, and scale intensity.", + "version": "1.6.2", + "description": [ + "Shake an object (position, angle or scale).", + "", + "It can be used for:", + "- Hit or impact", + "- Object slowly rocking back and forth (like a ship)", + "- Simulate engine vibration, earthquake, or pulsing", + "", + "The top-down RPG example uses this extension for damage animations ([open the project online](https://editor.gdevelop.io/?project=example://top-down-rpg))." + ], + "origin": { + "identifier": "ShakeObject", + "name": "gdevelop-extension-store" + }, + "tags": [ + "shaking", + "object", + "effect", + "shake", + "scale", + "position", + "rotate", + "angle" + ], + "authorIds": [ + "gqDaZjCfevOOxBYkK6zlhtZnXCg1" + ], + "dependencies": [], + "globalVariables": [], + "sceneVariables": [], + "eventsFunctions": [], + "eventsFunctionsFolderStructure": { + "folderName": "__ROOT" + }, + "eventsBasedBehaviors": [ + { + "description": "Shake objects with translation and rotation.", + "fullName": "Shake object (position, angle)", + "helpPath": "", + "iconUrl": "", + "name": "ShakeObject_PositionAngle", + "objectType": "", + "previewIconUrl": "", + "eventsFunctions": [ + { + "description": "Shake an object, using one or more ways to shake (position, angle). Make sure to \"Stop shaking\" before starting a new shake if it uses different parameters. ", + "fullName": "Shake object (position, angle)", + "functionType": "Action", + "name": "ShakeObject_PositionAngle", + "sentence": "Shake object _PARAM0_ for _PARAM2_ seconds. Modify position amplitude _PARAM3_ on X axis and _PARAM4_ on Y axis, angle rotation amplitude _PARAM5_. Wait _PARAM6_ seconds between shakes. Keep shaking until stopped: _PARAM7_", + "events": [ + { + "colorB": 228, + "colorG": 176, + "colorR": 74, + "creationTime": 0, + "name": "Shake Object", + "source": "", + "type": "BuiltinCommonInstructions::Group", + "events": [ + { + "type": "BuiltinCommonInstructions::Comment", + "color": { + "b": 109, + "g": 230, + "r": 255, + "textB": 0, + "textG": 0, + "textR": 0 + }, + "comment": "Start/Reset duration timer" + }, + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [], + "actions": [ + { + "type": { + "value": "ResetObjectTimer" + }, + "parameters": [ + "Object", + "\"__ShakeObject_DurationTimer\"" + ] + } + ] + }, + { + "type": "BuiltinCommonInstructions::Comment", + "color": { + "b": 109, + "g": 230, + "r": 255, + "textB": 0, + "textG": 0, + "textR": 0 + }, + "comment": "Pass input parameters to global variables so that onScenePostEvents can use them" + }, + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [], + "actions": [ + { + "type": { + "value": "ModVarObjet" + }, + "parameters": [ + "Object", + "__ShakeObject_Duration", + "=", + "Duration" + ] + }, + { + "type": { + "value": "ModVarObjet" + }, + "parameters": [ + "Object", + "__ShakeObject_PowerX", + "=", + "PowerX" + ] + }, + { + "type": { + "value": "ModVarObjet" + }, + "parameters": [ + "Object", + "__ShakeObject_PowerY", + "=", + "PowerY" + ] + }, + { + "type": { + "value": "ModVarObjet" + }, + "parameters": [ + "Object", + "__ShakeObject_PowerAngle", + "=", + "PowerAngle" + ] + }, + { + "type": { + "value": "ModVarObjet" + }, + "parameters": [ + "Object", + "__ShakeObject_TimeBetweenShakes", + "=", + "TimeBetweenShakes" + ] + } + ] + }, + { + "type": "BuiltinCommonInstructions::Comment", + "color": { + "b": 109, + "g": 230, + "r": 255, + "textB": 0, + "textG": 0, + "textR": 0 + }, + "comment": "Determine if the shake should keep going until stopped" + }, + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [], + "actions": [ + { + "type": { + "value": "SetObjectVariableAsBoolean" + }, + "parameters": [ + "Object", + "__ShakeObject_ShakeForever", + "False" + ] + } + ] + }, + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [ + { + "type": { + "value": "BooleanVariable" + }, + "parameters": [ + "ShakeForever", + "True", + "" + ] + } + ], + "actions": [ + { + "type": { + "value": "SetObjectVariableAsBoolean" + }, + "parameters": [ + "Object", + "__ShakeObject_ShakeForever", + "True" + ] + } + ] + }, + { + "type": "BuiltinCommonInstructions::Comment", + "color": { + "b": 109, + "g": 230, + "r": 255, + "textB": 0, + "textG": 0, + "textR": 0 + }, + "comment": "Add default values if none were provided" + }, + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [ + { + "type": { + "value": "VarObjet" + }, + "parameters": [ + "Object", + "__ShakeObject_Duration", + "=", + "0" + ] + } + ], + "actions": [ + { + "type": { + "value": "ModVarObjet" + }, + "parameters": [ + "Object", + "__ShakeObject_Duration", + "=", + "0.5" + ] + } + ] + }, + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [ + { + "type": { + "value": "VarObjet" + }, + "parameters": [ + "Object", + "__ShakeObject_TimeBetweenShakes", + "=", + "0" + ] + } + ], + "actions": [ + { + "type": { + "value": "ModVarObjet" + }, + "parameters": [ + "Object", + "__ShakeObject_TimeBetweenShakes", + "=", + "0.08" + ] + } + ] + }, + { + "type": "BuiltinCommonInstructions::Comment", + "color": { + "b": 109, + "g": 230, + "r": 255, + "textB": 0, + "textG": 0, + "textR": 0 + }, + "comment": "If duration is less than a single shake, increase duration to make 1 full shake" + }, + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [ + { + "type": { + "value": "VarObjet" + }, + "parameters": [ + "Object", + "__ShakeObject_Duration", + "<", + "Object.Variable(__ShakeObject_TimeBetweenShakes)" + ] + } + ], + "actions": [ + { + "type": { + "value": "ModVarObjet" + }, + "parameters": [ + "Object", + "__ShakeObject_Duration", + "=", + "Object.Variable(__ShakeObject_TimeBetweenShakes)" + ] + } + ] + }, + { + "type": "BuiltinCommonInstructions::Comment", + "color": { + "b": 109, + "g": 230, + "r": 255, + "textB": 0, + "textG": 0, + "textR": 0 + }, + "comment": "Detect initial shake" + }, + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [ + { + "type": { + "value": "VarObjet" + }, + "parameters": [ + "Object", + "__ShakeObject_ShakeInProgress", + "=", + "0" + ] + } + ], + "actions": [ + { + "type": { + "value": "ModVarObjet" + }, + "parameters": [ + "Object", + "__ShakeObject_InitialShake", + "=", + "1" + ] + } + ] + }, + { + "type": "BuiltinCommonInstructions::Comment", + "color": { + "b": 109, + "g": 230, + "r": 255, + "textB": 0, + "textG": 0, + "textR": 0 + }, + "comment": "Initiate the onScenePostEvents function" + }, + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [], + "actions": [ + { + "type": { + "value": "ModVarObjet" + }, + "parameters": [ + "Object", + "__ShakeObject_ShakeInProgress", + "=", + "1" + ] + } + ] + } + ], + "parameters": [] + } + ], + "parameters": [ + { + "description": "Object", + "name": "Object", + "type": "object" + }, + { + "description": "Behavior", + "name": "Behavior", + "supplementaryInformation": "ShakeObject::ShakeObject_PositionAngle", + "type": "behavior" + }, + { + "description": "Duration of shake (in seconds) (Default: 0.5) ", + "name": "Duration", + "type": "expression" + }, + { + "description": "Amplitude of postion shake in X direction (in pixels) (For example: 5)", + "name": "PowerX", + "type": "expression" + }, + { + "description": "Amplitude of position shake in Y direction (in pixels) (For example: 5)", + "longDescription": "Use a negative number to make the single-shake move in the opposite direction.", + "name": "PowerY", + "type": "expression" + }, + { + "description": "Amplitude of angle rotation shake (in degrees) (For example: 5)", + "name": "PowerAngle", + "type": "expression" + }, + { + "description": "Amount of time between shakes (in seconds) (Default: 0.08)", + "longDescription": "For a single-shake effect, set it to the same value as \"Duration\".", + "name": "TimeBetweenShakes", + "type": "expression" + }, + { + "description": "Keep shaking until stopped", + "longDescription": "Duration value will be ignored", + "name": "ShakeForever", + "type": "yesorno" + } + ], + "objectGroups": [] + }, + { + "fullName": "", + "functionType": "Action", + "name": "doStepPostEvents", + "sentence": "", + "events": [ + { + "colorB": 228, + "colorG": 176, + "colorR": 74, + "creationTime": 0, + "name": "Shake Object", + "source": "", + "type": "BuiltinCommonInstructions::Group", + "events": [ + { + "colorB": 228, + "colorG": 176, + "colorR": 74, + "creationTime": 0, + "name": "Start shaking", + "source": "", + "type": "BuiltinCommonInstructions::Group", + "events": [ + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [ + { + "type": { + "value": "VarObjet" + }, + "parameters": [ + "Object", + "__ShakeObject_ShakeInProgress", + "=", + "1" + ] + } + ], + "actions": [], + "events": [ + { + "type": "BuiltinCommonInstructions::Comment", + "color": { + "b": 109, + "g": 230, + "r": 255, + "textB": 0, + "textG": 0, + "textR": 0 + }, + "comment": "Keep object shaking forever (if desired)" + }, + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [ + { + "type": { + "value": "ObjectVariableAsBoolean" + }, + "parameters": [ + "Object", + "__ShakeObject_ShakeForever", + "True" + ] + } + ], + "actions": [ + { + "type": { + "value": "ModVarObjet" + }, + "parameters": [ + "Object", + "__ShakeObject_Duration", + "=", + "100" + ] + }, + { + "type": { + "value": "ResetObjectTimer" + }, + "parameters": [ + "Object", + "\"__ShakeObject_DurationTimer\"" + ] + } + ] + }, + { + "colorB": 228, + "colorG": 176, + "colorR": 74, + "creationTime": 0, + "name": "Calculate movement of the shake", + "source": "", + "type": "BuiltinCommonInstructions::Group", + "events": [ + { + "type": "BuiltinCommonInstructions::Comment", + "color": { + "b": 109, + "g": 230, + "r": 255, + "textB": 0, + "textG": 0, + "textR": 0 + }, + "comment": "Run once before every shake movement" + }, + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [ + { + "type": { + "value": "BuiltinCommonInstructions::Or" + }, + "parameters": [], + "subInstructions": [ + { + "type": { + "value": "VarObjet" + }, + "parameters": [ + "Object", + "__ShakeObject_InitialShake", + "=", + "1" + ] + }, + { + "type": { + "value": "ObjectTimer" + }, + "parameters": [ + "Object", + "\"__ShakeObject_ShakeTimer\"", + "Object.Variable(__ShakeObject_TimeBetweenShakes)" + ] + } + ] + } + ], + "actions": [ + { + "type": { + "value": "ResetObjectTimer" + }, + "parameters": [ + "Object", + "\"__ShakeObject_ShakeTimer\"" + ] + }, + { + "type": { + "value": "ModVarObjet" + }, + "parameters": [ + "Object", + "__ShakeObject_ShakeCounter", + "+", + "1" + ] + } + ], + "events": [ + { + "colorB": 228, + "colorG": 176, + "colorR": 74, + "creationTime": 0, + "name": "Correct for drift and reset drift tracking variables", + "source": "", + "type": "BuiltinCommonInstructions::Group", + "events": [ + { + "type": "BuiltinCommonInstructions::Comment", + "color": { + "b": 109, + "g": 230, + "r": 255, + "textB": 0, + "textG": 0, + "textR": 0 + }, + "comment": "Move to correct any drift from previous shake" + }, + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [ + { + "type": { + "value": "BuiltinCommonInstructions::Or" + }, + "parameters": [], + "subInstructions": [ + { + "type": { + "value": "VarObjet" + }, + "parameters": [ + "Object", + "__ShakeObject_PowerX", + "!=", + "0" + ] + }, + { + "type": { + "value": "VarObjet" + }, + "parameters": [ + "Object", + "__ShakeObject_PowerY", + "!=", + "0" + ] + } + ] + } + ], + "actions": [ + { + "type": { + "value": "SetX" + }, + "parameters": [ + "Object", + "-", + "Object.Variable(__ShakeObject_DisplacementTravelledX)" + ] + }, + { + "type": { + "value": "SetY" + }, + "parameters": [ + "Object", + "-", + "Object.Variable(__ShakeObject_DisplacementTravelledY)" + ] + } + ] + }, + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [ + { + "type": { + "value": "VarObjet" + }, + "parameters": [ + "Object", + "__ShakeObject_PowerAngle", + "!=", + "0" + ] + } + ], + "actions": [ + { + "type": { + "value": "SetAngle" + }, + "parameters": [ + "Object", + "-", + "Object.Variable(__ShakeObject_AngleTravelled)" + ] + } + ] + }, + { + "type": "BuiltinCommonInstructions::Comment", + "color": { + "b": 109, + "g": 230, + "r": 255, + "textB": 0, + "textG": 0, + "textR": 0 + }, + "comment": "Reset drift detection variables" + }, + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [], + "actions": [ + { + "type": { + "value": "ModVarObjet" + }, + "parameters": [ + "Object", + "__ShakeObject_DisplacementTravelledX", + "=", + "0" + ] + }, + { + "type": { + "value": "ModVarObjet" + }, + "parameters": [ + "Object", + "__ShakeObject_DisplacementTravelledY", + "=", + "0" + ] + }, + { + "type": { + "value": "ModVarObjet" + }, + "parameters": [ + "Object", + "__ShakeObject_AngleTravelled", + "=", + "0" + ] + } + ] + } + ], + "parameters": [] + }, + { + "type": "BuiltinCommonInstructions::Comment", + "color": { + "b": 109, + "g": 230, + "r": 255, + "textB": 0, + "textG": 0, + "textR": 0 + }, + "comment": "Calculate Position Shake" + }, + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [ + { + "type": { + "value": "BuiltinCommonInstructions::Or" + }, + "parameters": [], + "subInstructions": [ + { + "type": { + "value": "VarObjet" + }, + "parameters": [ + "Object", + "__ShakeObject_PowerX", + "!=", + "0" + ] + }, + { + "type": { + "value": "VarObjet" + }, + "parameters": [ + "Object", + "__ShakeObject_PowerY", + "!=", + "0" + ] + } + ] + } + ], + "actions": [], + "events": [ + { + "type": "BuiltinCommonInstructions::Comment", + "color": { + "b": 109, + "g": 230, + "r": 255, + "textB": 0, + "textG": 0, + "textR": 0 + }, + "comment": "PositionDisplacement = (DesiredDuration - RunningTimer) / DesiredDuration * Amplitude * [-1 or 1]" + }, + { + "type": "BuiltinCommonInstructions::Comment", + "color": { + "b": 109, + "g": 230, + "r": 255, + "textB": 0, + "textG": 0, + "textR": 0 + }, + "comment": "Make initial shake NOT random so users can set a direction for a one-shake effect" + }, + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [ + { + "type": { + "value": "VarObjet" + }, + "parameters": [ + "Object", + "__ShakeObject_InitialShake", + "=", + "1" + ] + } + ], + "actions": [], + "events": [ + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [ + { + "type": { + "value": "VarObjet" + }, + "parameters": [ + "Object", + "__ShakeObject_PowerX", + "!=", + "0" + ] + } + ], + "actions": [ + { + "type": { + "value": "ModVarObjet" + }, + "parameters": [ + "Object", + "__ShakeObject_DisplacementX", + "=", + "(Object.Variable(__ShakeObject_Duration) - Object.ObjectTimerElapsedTime(\"__ShakeObject_DurationTimer\")) / Object.Variable(__ShakeObject_Duration) * Object.Variable(__ShakeObject_PowerX)" + ] + } + ] + }, + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [ + { + "type": { + "value": "VarObjet" + }, + "parameters": [ + "Object", + "__ShakeObject_PowerY", + "!=", + "0" + ] + } + ], + "actions": [ + { + "type": { + "value": "ModVarObjet" + }, + "parameters": [ + "Object", + "__ShakeObject_DisplacementY", + "=", + "(Object.Variable(__ShakeObject_Duration) - Object.ObjectTimerElapsedTime(\"__ShakeObject_DurationTimer\")) / Object.Variable(__ShakeObject_Duration) * Object.Variable(__ShakeObject_PowerY)" + ] + } + ] + } + ] + }, + { + "type": "BuiltinCommonInstructions::Comment", + "color": { + "b": 109, + "g": 230, + "r": 255, + "textB": 0, + "textG": 0, + "textR": 0 + }, + "comment": "After initial shake pick a random direction" + }, + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [ + { + "type": { + "value": "VarObjet" + }, + "parameters": [ + "Object", + "__ShakeObject_InitialShake", + "=", + "0" + ] + } + ], + "actions": [], + "events": [ + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [ + { + "type": { + "value": "VarObjet" + }, + "parameters": [ + "Object", + "__ShakeObject_PowerX", + "!=", + "0" + ] + } + ], + "actions": [ + { + "type": { + "value": "ModVarObjet" + }, + "parameters": [ + "Object", + "__ShakeObject_DisplacementX", + "=", + "(Object.Variable(__ShakeObject_Duration) - Object.ObjectTimerElapsedTime(\"__ShakeObject_DurationTimer\")) / Object.Variable(__ShakeObject_Duration) * Object.Variable(__ShakeObject_PowerX) * RandomWithStep(-1, 1, 2)" + ] + } + ] + }, + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [ + { + "type": { + "value": "VarObjet" + }, + "parameters": [ + "Object", + "__ShakeObject_PowerY", + "!=", + "0" + ] + } + ], + "actions": [ + { + "type": { + "value": "ModVarObjet" + }, + "parameters": [ + "Object", + "__ShakeObject_DisplacementY", + "=", + "(Object.Variable(__ShakeObject_Duration) - Object.ObjectTimerElapsedTime(\"__ShakeObject_DurationTimer\")) / Object.Variable(__ShakeObject_Duration) * Object.Variable(__ShakeObject_PowerY) * RandomWithStep(-1, 1, 2)" + ] + } + ] + } + ] + } + ] + }, + { + "type": "BuiltinCommonInstructions::Comment", + "color": { + "b": 109, + "g": 230, + "r": 255, + "textB": 0, + "textG": 0, + "textR": 0 + }, + "comment": "Calculate Rotation (angle) shake " + }, + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [ + { + "type": { + "value": "VarObjet" + }, + "parameters": [ + "Object", + "__ShakeObject_PowerAngle", + "!=", + "0" + ] + } + ], + "actions": [], + "events": [ + { + "type": "BuiltinCommonInstructions::Comment", + "color": { + "b": 109, + "g": 230, + "r": 255, + "textB": 0, + "textG": 0, + "textR": 0 + }, + "comment": "Every \"even\" shake, rotate clockwise" + }, + { + "type": "BuiltinCommonInstructions::Comment", + "color": { + "b": 109, + "g": 230, + "r": 255, + "textB": 0, + "textG": 0, + "textR": 0 + }, + "comment": "AngleDisplacement = (DesiredDuration - RunningTimer) / DesiredDuration * Amplitude" + }, + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [ + { + "type": { + "value": "BuiltinCommonInstructions::CompareNumbers" + }, + "parameters": [ + "mod(Object.Variable(__ShakeObject_ShakeCounter),2)", + "=", + "0" + ] + } + ], + "actions": [ + { + "type": { + "value": "ModVarObjet" + }, + "parameters": [ + "Object", + "__ShakeObject_DisplacementAngle", + "=", + "(Object.Variable(__ShakeObject_Duration) - Object.ObjectTimerElapsedTime(\"__ShakeObject_DurationTimer\")) / Object.Variable(__ShakeObject_Duration) * Object.Variable(__ShakeObject_PowerAngle)" + ] + } + ] + }, + { + "type": "BuiltinCommonInstructions::Comment", + "color": { + "b": 109, + "g": 230, + "r": 255, + "textB": 0, + "textG": 0, + "textR": 0 + }, + "comment": "Every \"odd\" shake, rotate counter-clockwise" + }, + { + "type": "BuiltinCommonInstructions::Comment", + "color": { + "b": 109, + "g": 230, + "r": 255, + "textB": 0, + "textG": 0, + "textR": 0 + }, + "comment": "AngleDisplacement = (DesiredDuration - RunningTimer) / DesiredDuration * Amplitude" + }, + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [ + { + "type": { + "value": "BuiltinCommonInstructions::CompareNumbers" + }, + "parameters": [ + "mod(Object.Variable(__ShakeObject_ShakeCounter),2)", + "=", + "1" + ] + } + ], + "actions": [ + { + "type": { + "value": "ModVarObjet" + }, + "parameters": [ + "Object", + "__ShakeObject_DisplacementAngle", + "=", + "-1 *(Object.Variable(__ShakeObject_Duration) - Object.ObjectTimerElapsedTime(\"__ShakeObject_DurationTimer\")) / Object.Variable(__ShakeObject_Duration) * Object.Variable(__ShakeObject_PowerAngle)" + ] + } + ] + } + ] + }, + { + "type": "BuiltinCommonInstructions::Comment", + "color": { + "b": 109, + "g": 230, + "r": 255, + "textB": 0, + "textG": 0, + "textR": 0 + }, + "comment": "Save that initial shake has been processed" + }, + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [], + "actions": [ + { + "type": { + "value": "ModVarObjet" + }, + "parameters": [ + "Object", + "__ShakeObject_InitialShake", + "=", + "0" + ] + } + ] + } + ] + } + ], + "parameters": [] + }, + { + "colorB": 228, + "colorG": 176, + "colorR": 74, + "creationTime": 0, + "name": "Move object", + "source": "", + "type": "BuiltinCommonInstructions::Group", + "events": [ + { + "type": "BuiltinCommonInstructions::Comment", + "color": { + "b": 109, + "g": 230, + "r": 255, + "textB": 0, + "textG": 0, + "textR": 0 + }, + "comment": "Calculate the fraction of shake that occured during this frame" + }, + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [], + "actions": [ + { + "type": { + "value": "ModVarObjet" + }, + "parameters": [ + "Object", + "__ShakeObject_PercentTimeElapsedThisFrame", + "=", + "min(1,TimeDelta()/Object.Variable(__ShakeObject_TimeBetweenShakes))" + ] + } + ] + }, + { + "type": "BuiltinCommonInstructions::Comment", + "color": { + "b": 109, + "g": 230, + "r": 255, + "textB": 0, + "textG": 0, + "textR": 0 + }, + "comment": "Increase change for the first half of the shake (move away from original values)" + }, + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [ + { + "type": { + "inverted": true, + "value": "ObjectTimer" + }, + "parameters": [ + "Object", + "\"__ShakeObject_ShakeTimer\"", + "Object.Variable(__ShakeObject_TimeBetweenShakes)/2" + ] + } + ], + "actions": [], + "events": [ + { + "type": "BuiltinCommonInstructions::Comment", + "color": { + "b": 109, + "g": 230, + "r": 255, + "textB": 0, + "textG": 0, + "textR": 0 + }, + "comment": "Change position" + }, + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [ + { + "type": { + "value": "BuiltinCommonInstructions::Or" + }, + "parameters": [], + "subInstructions": [ + { + "type": { + "value": "VarObjet" + }, + "parameters": [ + "Object", + "__ShakeObject_PowerX", + "!=", + "0" + ] + }, + { + "type": { + "value": "VarObjet" + }, + "parameters": [ + "Object", + "__ShakeObject_PowerY", + "!=", + "0" + ] + } + ] + } + ], + "actions": [ + { + "type": { + "value": "SetX" + }, + "parameters": [ + "Object", + "+", + "round(1024 * Object.Variable(__ShakeObject_DisplacementX) * Object.Variable(__ShakeObject_PercentTimeElapsedThisFrame)) / 1024" + ] + }, + { + "type": { + "value": "SetY" + }, + "parameters": [ + "Object", + "+", + "round(1024 * Object.Variable(__ShakeObject_DisplacementY) * Object.Variable(__ShakeObject_PercentTimeElapsedThisFrame)) / 1024" + ] + } + ], + "events": [ + { + "type": "BuiltinCommonInstructions::Comment", + "color": { + "b": 109, + "g": 230, + "r": 255, + "textB": 0, + "textG": 0, + "textR": 0 + }, + "comment": "Save movement to calculate drift" + }, + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [], + "actions": [ + { + "type": { + "value": "ModVarObjet" + }, + "parameters": [ + "Object", + "__ShakeObject_DisplacementTravelledX", + "+", + "round(1024 * Object.Variable(__ShakeObject_DisplacementX) * Object.Variable(__ShakeObject_PercentTimeElapsedThisFrame)) / 1024" + ] + }, + { + "type": { + "value": "ModVarObjet" + }, + "parameters": [ + "Object", + "__ShakeObject_DisplacementTravelledY", + "+", + "round(1024 * Object.Variable(__ShakeObject_DisplacementY) * Object.Variable(__ShakeObject_PercentTimeElapsedThisFrame)) / 1024" + ] + } + ] + } + ] + }, + { + "type": "BuiltinCommonInstructions::Comment", + "color": { + "b": 109, + "g": 230, + "r": 255, + "textB": 0, + "textG": 0, + "textR": 0 + }, + "comment": "Change angle" + }, + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [ + { + "type": { + "value": "BuiltinCommonInstructions::CompareNumbers" + }, + "parameters": [ + "Object.Variable(__ShakeObject_PowerAngle)", + "!=", + "0" + ] + } + ], + "actions": [ + { + "type": { + "value": "SetAngle" + }, + "parameters": [ + "Object", + "+", + "round(1024 * Object.Variable(__ShakeObject_DisplacementAngle) * Object.Variable(__ShakeObject_PercentTimeElapsedThisFrame)) / 1024" + ] + } + ], + "events": [ + { + "type": "BuiltinCommonInstructions::Comment", + "color": { + "b": 109, + "g": 230, + "r": 255, + "textB": 0, + "textG": 0, + "textR": 0 + }, + "comment": "Save movement to calculate drift" + }, + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [], + "actions": [ + { + "type": { + "value": "ModVarObjet" + }, + "parameters": [ + "Object", + "__ShakeObject_AngleTravelled", + "+", + "round(1024 * Object.Variable(__ShakeObject_DisplacementAngle) * Object.Variable(__ShakeObject_PercentTimeElapsedThisFrame)) / 1024" + ] + } + ] + } + ] + } + ] + }, + { + "type": "BuiltinCommonInstructions::Comment", + "color": { + "b": 109, + "g": 230, + "r": 255, + "textB": 0, + "textG": 0, + "textR": 0 + }, + "comment": "Decrease change the second half of the shake (return to original position)" + }, + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [ + { + "type": { + "value": "ObjectTimer" + }, + "parameters": [ + "Object", + "\"__ShakeObject_ShakeTimer\"", + "Object.Variable(__ShakeObject_TimeBetweenShakes)/2" + ] + } + ], + "actions": [], + "events": [ + { + "type": "BuiltinCommonInstructions::Comment", + "color": { + "b": 109, + "g": 230, + "r": 255, + "textB": 0, + "textG": 0, + "textR": 0 + }, + "comment": "Change position" + }, + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [ + { + "type": { + "value": "BuiltinCommonInstructions::Or" + }, + "parameters": [], + "subInstructions": [ + { + "type": { + "value": "VarObjet" + }, + "parameters": [ + "Object", + "__ShakeObject_PowerX", + "!=", + "0" + ] + }, + { + "type": { + "value": "VarObjet" + }, + "parameters": [ + "Object", + "__ShakeObject_PowerY", + "!=", + "0" + ] + } + ] + } + ], + "actions": [ + { + "type": { + "value": "SetX" + }, + "parameters": [ + "Object", + "-", + "round(1024 * Object.Variable(__ShakeObject_DisplacementX) * Object.Variable(__ShakeObject_PercentTimeElapsedThisFrame)) / 1024" + ] + }, + { + "type": { + "value": "SetY" + }, + "parameters": [ + "Object", + "-", + "round(1024 * Object.Variable(__ShakeObject_DisplacementY) * Object.Variable(__ShakeObject_PercentTimeElapsedThisFrame)) / 1024" + ] + } + ], + "events": [ + { + "type": "BuiltinCommonInstructions::Comment", + "color": { + "b": 109, + "g": 230, + "r": 255, + "textB": 0, + "textG": 0, + "textR": 0 + }, + "comment": "Save movement to calculate drift" + }, + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [], + "actions": [ + { + "type": { + "value": "ModVarObjet" + }, + "parameters": [ + "Object", + "__ShakeObject_DisplacementTravelledX", + "-", + "round(1024 * Object.Variable(__ShakeObject_DisplacementX) * Object.Variable(__ShakeObject_PercentTimeElapsedThisFrame)) / 1024" + ] + }, + { + "type": { + "value": "ModVarObjet" + }, + "parameters": [ + "Object", + "__ShakeObject_DisplacementTravelledY", + "-", + "round(1024 * Object.Variable(__ShakeObject_DisplacementY) * Object.Variable(__ShakeObject_PercentTimeElapsedThisFrame)) / 1024" + ] + } + ] + } + ] + }, + { + "type": "BuiltinCommonInstructions::Comment", + "color": { + "b": 109, + "g": 230, + "r": 255, + "textB": 0, + "textG": 0, + "textR": 0 + }, + "comment": "Change angle" + }, + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [ + { + "type": { + "value": "BuiltinCommonInstructions::CompareNumbers" + }, + "parameters": [ + "Object.Variable(__ShakeObject_PowerAngle)", + "!=", + "0" + ] + } + ], + "actions": [ + { + "type": { + "value": "SetAngle" + }, + "parameters": [ + "Object", + "-", + "round(1024 * Object.Variable(__ShakeObject_DisplacementAngle) * Object.Variable(__ShakeObject_PercentTimeElapsedThisFrame)) / 1024" + ] + } + ], + "events": [ + { + "type": "BuiltinCommonInstructions::Comment", + "color": { + "b": 109, + "g": 230, + "r": 255, + "textB": 0, + "textG": 0, + "textR": 0 + }, + "comment": "Save movement to calculate drift" + }, + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [], + "actions": [ + { + "type": { + "value": "ModVarObjet" + }, + "parameters": [ + "Object", + "__ShakeObject_AngleTravelled", + "-", + "round(1024 * Object.Variable(__ShakeObject_DisplacementAngle) * Object.Variable(__ShakeObject_PercentTimeElapsedThisFrame)) / 1024" + ] + } + ] + } + ] + } + ] + } + ], + "parameters": [] + } + ] + } + ], + "parameters": [] + }, + { + "colorB": 228, + "colorG": 176, + "colorR": 74, + "creationTime": 0, + "name": "Stop shaking", + "source": "", + "type": "BuiltinCommonInstructions::Group", + "events": [ + { + "type": "BuiltinCommonInstructions::Comment", + "color": { + "b": 109, + "g": 230, + "r": 255, + "textB": 0, + "textG": 0, + "textR": 0 + }, + "comment": "Stop shaking when the duration has been reached (or if the stop shaking function was called)" + }, + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [ + { + "type": { + "value": "BuiltinCommonInstructions::Or" + }, + "parameters": [], + "subInstructions": [ + { + "type": { + "value": "ObjectTimer" + }, + "parameters": [ + "Object", + "\"__ShakeObject_DurationTimer\"", + "Object.Variable(__ShakeObject_Duration)" + ] + }, + { + "type": { + "value": "VarObjet" + }, + "parameters": [ + "Object", + "__ShakeObject_ShakeInProgress", + "=", + "-1" + ] + } + ] + }, + { + "type": { + "value": "BuiltinCommonInstructions::Once" + }, + "parameters": [] + } + ], + "actions": [ + { + "type": { + "value": "ModVarObjet" + }, + "parameters": [ + "Object", + "__ShakeObject_ShakeInProgress", + "=", + "0" + ] + }, + { + "type": { + "value": "ModVarObjet" + }, + "parameters": [ + "Object", + "__ShakeObject_ShakeCounter", + "=", + "0" + ] + } + ], + "events": [ + { + "colorB": 228, + "colorG": 176, + "colorR": 74, + "creationTime": 0, + "name": "Correct for drift and reset drift tracking variables", + "source": "", + "type": "BuiltinCommonInstructions::Group", + "events": [ + { + "type": "BuiltinCommonInstructions::Comment", + "color": { + "b": 109, + "g": 230, + "r": 255, + "textB": 0, + "textG": 0, + "textR": 0 + }, + "comment": "Move to correct any drift from previous shake" + }, + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [ + { + "type": { + "value": "BuiltinCommonInstructions::Or" + }, + "parameters": [], + "subInstructions": [ + { + "type": { + "value": "VarObjet" + }, + "parameters": [ + "Object", + "__ShakeObject_PowerX", + "!=", + "0" + ] + }, + { + "type": { + "value": "VarObjet" + }, + "parameters": [ + "Object", + "__ShakeObject_PowerY", + "!=", + "0" + ] + } + ] + } + ], + "actions": [ + { + "type": { + "value": "SetX" + }, + "parameters": [ + "Object", + "-", + "Object.Variable(__ShakeObject_DisplacementTravelledX)" + ] + }, + { + "type": { + "value": "SetY" + }, + "parameters": [ + "Object", + "-", + "Object.Variable(__ShakeObject_DisplacementTravelledY)" + ] + } + ] + }, + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [ + { + "type": { + "value": "VarObjet" + }, + "parameters": [ + "Object", + "__ShakeObject_PowerAngle", + "!=", + "0" + ] + } + ], + "actions": [ + { + "type": { + "value": "SetAngle" + }, + "parameters": [ + "Object", + "-", + "Object.Variable(__ShakeObject_AngleTravelled)" + ] + } + ] + }, + { + "type": "BuiltinCommonInstructions::Comment", + "color": { + "b": 109, + "g": 230, + "r": 255, + "textB": 0, + "textG": 0, + "textR": 0 + }, + "comment": "Reset drift detection variables" + }, + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [], + "actions": [ + { + "type": { + "value": "ModVarObjet" + }, + "parameters": [ + "Object", + "__ShakeObject_DisplacementTravelledX", + "=", + "0" + ] + }, + { + "type": { + "value": "ModVarObjet" + }, + "parameters": [ + "Object", + "__ShakeObject_DisplacementTravelledY", + "=", + "0" + ] + }, + { + "type": { + "value": "ModVarObjet" + }, + "parameters": [ + "Object", + "__ShakeObject_AngleTravelled", + "=", + "0" + ] + } + ] + } + ], + "parameters": [] + } + ] + } + ], + "parameters": [] + } + ], + "parameters": [] + } + ], + "parameters": [ + { + "description": "Object", + "name": "Object", + "type": "object" + }, + { + "description": "Behavior", + "name": "Behavior", + "supplementaryInformation": "ShakeObject::ShakeObject_PositionAngle", + "type": "behavior" + } + ], + "objectGroups": [] + }, + { + "description": "Stop any shaking of object that was initiated by the Shake Object extension.", + "fullName": "Stop shaking the object", + "functionType": "Action", + "name": "StopShaking", + "sentence": "Stop shaking _PARAM0_", + "events": [ + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [ + { + "type": { + "value": "ShakeObject::ShakeObject_PositionAngle::IsShaking" + }, + "parameters": [ + "Object", + "Behavior", + "" + ] + } + ], + "actions": [ + { + "type": { + "value": "ModVarObjet" + }, + "parameters": [ + "Object", + "__ShakeObject_ShakeInProgress", + "=", + "-1" + ] + } + ] + } + ], + "parameters": [ + { + "description": "Object", + "name": "Object", + "type": "object" + }, + { + "description": "Behavior", + "name": "Behavior", + "supplementaryInformation": "ShakeObject::ShakeObject_PositionAngle", + "type": "behavior" + } + ], + "objectGroups": [] + }, + { + "description": "Check if an object is shaking.", + "fullName": "Check if an object is shaking", + "functionType": "Condition", + "name": "IsShaking", + "sentence": "_PARAM0_ is shaking", + "events": [ + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [ + { + "type": { + "value": "VarObjet" + }, + "parameters": [ + "Object", + "__ShakeObject_ShakeInProgress", + "=", + "1" + ] + } + ], + "actions": [ + { + "type": { + "value": "SetReturnBoolean" + }, + "parameters": [ + "True" + ] + } + ] + } + ], + "parameters": [ + { + "description": "Object", + "name": "Object", + "type": "object" + }, + { + "description": "Behavior", + "name": "Behavior", + "supplementaryInformation": "ShakeObject::ShakeObject_PositionAngle", + "type": "behavior" + } + ], + "objectGroups": [] + } + ], + "eventsFunctionsFolderStructure": { + "folderName": "__ROOT", + "children": [ + { + "functionName": "ShakeObject_PositionAngle" + }, + { + "functionName": "doStepPostEvents" + }, + { + "functionName": "StopShaking" + }, + { + "functionName": "IsShaking" + } + ] + }, + "propertyDescriptors": [], + "propertiesFolderStructure": { + "folderName": "__ROOT" + } + }, + { + "description": "Shake objects with translation, rotation and scale.", + "fullName": "Shake object (position, angle, scale)", + "helpPath": "", + "iconUrl": "", + "name": "ShakeObject_PositionAngleScale", + "objectType": "", + "previewIconUrl": "", + "eventsFunctions": [ + { + "description": "Shake an object, using one or more ways to shake (position, angle, scale). Make sure to \"Stop shaking\" before starting a new shake if it uses different parameters.", + "fullName": "Shake object (position, angle, scale)", + "functionType": "Action", + "name": "ShakeObject_PositionAngleScale", + "sentence": "Shake object _PARAM0_ for _PARAM2_ seconds. Modify position amplitude _PARAM3_ on X axis and _PARAM4_ on Y axis, angle rotation amplitude _PARAM5_, and scale amplitude _PARAM6_. Wait _PARAM7_ seconds between shakes. Keep shaking until stopped: _PARAM8_", + "events": [ + { + "colorB": 228, + "colorG": 176, + "colorR": 74, + "creationTime": 0, + "name": "Shake Object", + "source": "", + "type": "BuiltinCommonInstructions::Group", + "events": [ + { + "type": "BuiltinCommonInstructions::Comment", + "color": { + "b": 109, + "g": 230, + "r": 255, + "textB": 0, + "textG": 0, + "textR": 0 + }, + "comment": "Start/Reset duration timer" + }, + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [], + "actions": [ + { + "type": { + "value": "ResetObjectTimer" + }, + "parameters": [ + "Object", + "\"__ShakeObject_DurationTimer\"" + ] + } + ] + }, + { + "type": "BuiltinCommonInstructions::Comment", + "color": { + "b": 109, + "g": 230, + "r": 255, + "textB": 0, + "textG": 0, + "textR": 0 + }, + "comment": "Pass input parameters to global variables so that onScenePostEvents can use them" + }, + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [], + "actions": [ + { + "type": { + "value": "ModVarObjet" + }, + "parameters": [ + "Object", + "__ShakeObject_Duration", + "=", + "Duration" + ] + }, + { + "type": { + "value": "ModVarObjet" + }, + "parameters": [ + "Object", + "__ShakeObject_PowerX", + "=", + "PowerX" + ] + }, + { + "type": { + "value": "ModVarObjet" + }, + "parameters": [ + "Object", + "__ShakeObject_PowerY", + "=", + "PowerY" + ] + }, + { + "type": { + "value": "ModVarObjet" + }, + "parameters": [ + "Object", + "__ShakeObject_PowerAngle", + "=", + "PowerAngle" + ] + }, + { + "type": { + "value": "ModVarObjet" + }, + "parameters": [ + "Object", + "__ShakeObject_PowerScale", + "=", + "PowerScale" + ] + }, + { + "type": { + "value": "ModVarObjet" + }, + "parameters": [ + "Object", + "__ShakeObject_TimeBetweenShakes", + "=", + "TimeBetweenShakes" + ] + } + ] + }, + { + "type": "BuiltinCommonInstructions::Comment", + "color": { + "b": 109, + "g": 230, + "r": 255, + "textB": 0, + "textG": 0, + "textR": 0 + }, + "comment": "Determine if the shake should keep going until stopped" + }, + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [], + "actions": [ + { + "type": { + "value": "SetObjectVariableAsBoolean" + }, + "parameters": [ + "Object", + "__ShakeObject_ShakeForever", + "False" + ] + } + ] + }, + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [ + { + "type": { + "value": "BooleanVariable" + }, + "parameters": [ + "ShakeForever", + "True", + "" + ] + } + ], + "actions": [ + { + "type": { + "value": "SetObjectVariableAsBoolean" + }, + "parameters": [ + "Object", + "__ShakeObject_ShakeForever", + "True" + ] + } + ] + }, + { + "type": "BuiltinCommonInstructions::Comment", + "color": { + "b": 109, + "g": 230, + "r": 255, + "textB": 0, + "textG": 0, + "textR": 0 + }, + "comment": "Add default values if none were provided" + }, + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [ + { + "type": { + "value": "VarObjet" + }, + "parameters": [ + "Object", + "__ShakeObject_Duration", + "=", + "0" + ] + } + ], + "actions": [ + { + "type": { + "value": "ModVarObjet" + }, + "parameters": [ + "Object", + "__ShakeObject_Duration", + "=", + "0.5" + ] + } + ] + }, + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [ + { + "type": { + "value": "VarObjet" + }, + "parameters": [ + "Object", + "__ShakeObject_TimeBetweenShakes", + "=", + "0" + ] + } + ], + "actions": [ + { + "type": { + "value": "ModVarObjet" + }, + "parameters": [ + "Object", + "__ShakeObject_TimeBetweenShakes", + "=", + "0.08" + ] + } + ] + }, + { + "type": "BuiltinCommonInstructions::Comment", + "color": { + "b": 109, + "g": 230, + "r": 255, + "textB": 0, + "textG": 0, + "textR": 0 + }, + "comment": "If duration is less than a single shake, increase duration to make 1 full shake" + }, + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [ + { + "type": { + "value": "VarObjet" + }, + "parameters": [ + "Object", + "__ShakeObject_Duration", + "<", + "Object.Variable(__ShakeObject_TimeBetweenShakes)" + ] + } + ], + "actions": [ + { + "type": { + "value": "ModVarObjet" + }, + "parameters": [ + "Object", + "__ShakeObject_Duration", + "=", + "Object.Variable(__ShakeObject_TimeBetweenShakes)" + ] + } + ] + }, + { + "type": "BuiltinCommonInstructions::Comment", + "color": { + "b": 109, + "g": 230, + "r": 255, + "textB": 0, + "textG": 0, + "textR": 0 + }, + "comment": "Detect initial shake" + }, + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [ + { + "type": { + "value": "VarObjet" + }, + "parameters": [ + "Object", + "__ShakeObject_ShakeInProgress", + "=", + "0" + ] + } + ], + "actions": [ + { + "type": { + "value": "ModVarObjet" + }, + "parameters": [ + "Object", + "__ShakeObject_InitialShake", + "=", + "1" + ] + } + ] + }, + { + "type": "BuiltinCommonInstructions::Comment", + "color": { + "b": 109, + "g": 230, + "r": 255, + "textB": 0, + "textG": 0, + "textR": 0 + }, + "comment": "Initiate the onScenePostEvents function" + }, + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [], + "actions": [ + { + "type": { + "value": "ModVarObjet" + }, + "parameters": [ + "Object", + "__ShakeObject_ShakeInProgress", + "=", + "1" + ] + } + ] + } + ], + "parameters": [] + } + ], + "parameters": [ + { + "description": "Object", + "name": "Object", + "type": "object" + }, + { + "description": "Behavior", + "name": "Behavior", + "supplementaryInformation": "ShakeObject::ShakeObject_PositionAngleScale", + "type": "behavior" + }, + { + "description": "Duration of shake (in seconds) (Default: 0.5)", + "name": "Duration", + "type": "expression" + }, + { + "description": "Amplitude of postion shake in X direction (in pixels) (For example: 5)", + "name": "PowerX", + "type": "expression" + }, + { + "description": "Amplitude of position shake in Y direction (in pixels) (For example: 5)", + "longDescription": "Use a negative number to make the single-shake move in the opposite direction.", + "name": "PowerY", + "type": "expression" + }, + { + "description": "Amplitude of angle rotation shake (in degrees) (For example: 5)", + "name": "PowerAngle", + "type": "expression" + }, + { + "description": "Amplitude of scale shake (in percent change) (For example: 5)", + "name": "PowerScale", + "type": "expression" + }, + { + "description": "Amount of time between shakes (in seconds) (Default: 0.08)", + "longDescription": "For a single-shake effect, set it to the same value as \"Duration\".", + "name": "TimeBetweenShakes", + "type": "expression" + }, + { + "description": "Keep shaking until stopped", + "longDescription": "Duration value will be ignored", + "name": "ShakeForever", + "type": "yesorno" + } + ], + "objectGroups": [] + }, + { + "fullName": "", + "functionType": "Action", + "name": "doStepPostEvents", + "sentence": "", + "events": [ + { + "colorB": 228, + "colorG": 176, + "colorR": 74, + "creationTime": 0, + "name": "Shake Object", + "source": "", + "type": "BuiltinCommonInstructions::Group", + "events": [ + { + "colorB": 228, + "colorG": 176, + "colorR": 74, + "creationTime": 0, + "name": "Start shaking", + "source": "", + "type": "BuiltinCommonInstructions::Group", + "events": [ + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [ + { + "type": { + "value": "VarObjet" + }, + "parameters": [ + "Object", + "__ShakeObject_ShakeInProgress", + "=", + "1" + ] + } + ], + "actions": [], + "events": [ + { + "type": "BuiltinCommonInstructions::Comment", + "color": { + "b": 109, + "g": 230, + "r": 255, + "textB": 0, + "textG": 0, + "textR": 0 + }, + "comment": "Keep object shaking forever (if desired)" + }, + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [ + { + "type": { + "value": "ObjectVariableAsBoolean" + }, + "parameters": [ + "Object", + "__ShakeObject_ShakeForever", + "True" + ] + } + ], + "actions": [ + { + "type": { + "value": "ModVarObjet" + }, + "parameters": [ + "Object", + "__ShakeObject_Duration", + "=", + "100" + ] + }, + { + "type": { + "value": "ResetObjectTimer" + }, + "parameters": [ + "Object", + "\"__ShakeObject_DurationTimer\"" + ] + } + ] + }, + { + "colorB": 228, + "colorG": 176, + "colorR": 74, + "creationTime": 0, + "name": "Calculate movement of the shake", + "source": "", + "type": "BuiltinCommonInstructions::Group", + "events": [ + { + "type": "BuiltinCommonInstructions::Comment", + "color": { + "b": 109, + "g": 230, + "r": 255, + "textB": 0, + "textG": 0, + "textR": 0 + }, + "comment": "Run once before every shake movement" + }, + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [ + { + "type": { + "value": "BuiltinCommonInstructions::Or" + }, + "parameters": [], + "subInstructions": [ + { + "type": { + "value": "VarObjet" + }, + "parameters": [ + "Object", + "__ShakeObject_InitialShake", + "=", + "1" + ] + }, + { + "type": { + "value": "ObjectTimer" + }, + "parameters": [ + "Object", + "\"__ShakeObject_ShakeTimer\"", + "Object.Variable(__ShakeObject_TimeBetweenShakes)" + ] + } + ] + } + ], + "actions": [ + { + "type": { + "value": "ResetObjectTimer" + }, + "parameters": [ + "Object", + "\"__ShakeObject_ShakeTimer\"" + ] + }, + { + "type": { + "value": "ModVarObjet" + }, + "parameters": [ + "Object", + "__ShakeObject_ShakeCounter", + "+", + "1" + ] + } + ], + "events": [ + { + "colorB": 228, + "colorG": 176, + "colorR": 74, + "creationTime": 0, + "folded": true, + "name": "Correct for drift and reset drift tracking variables", + "source": "", + "type": "BuiltinCommonInstructions::Group", + "events": [ + { + "type": "BuiltinCommonInstructions::Comment", + "color": { + "b": 109, + "g": 230, + "r": 255, + "textB": 0, + "textG": 0, + "textR": 0 + }, + "comment": "Move to correct any drift from previous shake" + }, + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [ + { + "type": { + "value": "BuiltinCommonInstructions::Or" + }, + "parameters": [], + "subInstructions": [ + { + "type": { + "value": "VarObjet" + }, + "parameters": [ + "Object", + "__ShakeObject_PowerX", + "!=", + "0" + ] + }, + { + "type": { + "value": "VarObjet" + }, + "parameters": [ + "Object", + "__ShakeObject_PowerY", + "!=", + "0" + ] + } + ] + } + ], + "actions": [ + { + "type": { + "value": "SetX" + }, + "parameters": [ + "Object", + "-", + "Object.Variable(__ShakeObject_DisplacementTravelledX)" + ] + }, + { + "type": { + "value": "SetY" + }, + "parameters": [ + "Object", + "-", + "Object.Variable(__ShakeObject_DisplacementTravelledY)" + ] + } + ] + }, + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [ + { + "type": { + "value": "VarObjet" + }, + "parameters": [ + "Object", + "__ShakeObject_PowerAngle", + "!=", + "0" + ] + } + ], + "actions": [ + { + "type": { + "value": "SetAngle" + }, + "parameters": [ + "Object", + "-", + "Object.Variable(__ShakeObject_AngleTravelled)" + ] + } + ] + }, + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [ + { + "type": { + "value": "VarObjet" + }, + "parameters": [ + "Object", + "__ShakeObject_PowerScale", + "!=", + "0" + ] + } + ], + "actions": [ + { + "type": { + "value": "ScalableCapability::ScalableBehavior::SetValue" + }, + "parameters": [ + "Object", + "Scale", + "-", + "Object.Variable(__ShakeObject_ScaleTravelled)" + ] + } + ] + }, + { + "type": "BuiltinCommonInstructions::Comment", + "color": { + "b": 109, + "g": 230, + "r": 255, + "textB": 0, + "textG": 0, + "textR": 0 + }, + "comment": "Reset drift detection variables" + }, + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [], + "actions": [ + { + "type": { + "value": "ModVarObjet" + }, + "parameters": [ + "Object", + "__ShakeObject_DisplacementTravelledX", + "=", + "0" + ] + }, + { + "type": { + "value": "ModVarObjet" + }, + "parameters": [ + "Object", + "__ShakeObject_DisplacementTravelledY", + "=", + "0" + ] + }, + { + "type": { + "value": "ModVarObjet" + }, + "parameters": [ + "Object", + "__ShakeObject_AngleTravelled", + "=", + "0" + ] + }, + { + "type": { + "value": "ModVarObjet" + }, + "parameters": [ + "Object", + "__ShakeObject_ScaleTravelled", + "=", + "0" + ] + } + ] + } + ], + "parameters": [] + }, + { + "type": "BuiltinCommonInstructions::Comment", + "color": { + "b": 109, + "g": 230, + "r": 255, + "textB": 0, + "textG": 0, + "textR": 0 + }, + "comment": "Calculate Position Shake" + }, + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [ + { + "type": { + "value": "BuiltinCommonInstructions::Or" + }, + "parameters": [], + "subInstructions": [ + { + "type": { + "value": "VarObjet" + }, + "parameters": [ + "Object", + "__ShakeObject_PowerX", + "!=", + "0" + ] + }, + { + "type": { + "value": "VarObjet" + }, + "parameters": [ + "Object", + "__ShakeObject_PowerY", + "!=", + "0" + ] + } + ] + } + ], + "actions": [], + "events": [ + { + "type": "BuiltinCommonInstructions::Comment", + "color": { + "b": 109, + "g": 230, + "r": 255, + "textB": 0, + "textG": 0, + "textR": 0 + }, + "comment": "PositionDisplacement = (DesiredDuration - RunningTimer) / DesiredDuration * Amplitude * [-1 or 1]" + }, + { + "type": "BuiltinCommonInstructions::Comment", + "color": { + "b": 109, + "g": 230, + "r": 255, + "textB": 0, + "textG": 0, + "textR": 0 + }, + "comment": "Make initial shake NOT random so users can set a direction for a one-shake effect" + }, + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [ + { + "type": { + "value": "VarObjet" + }, + "parameters": [ + "Object", + "__ShakeObject_InitialShake", + "=", + "1" + ] + } + ], + "actions": [], + "events": [ + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [ + { + "type": { + "value": "VarObjet" + }, + "parameters": [ + "Object", + "__ShakeObject_PowerX", + "!=", + "0" + ] + } + ], + "actions": [ + { + "type": { + "value": "ModVarObjet" + }, + "parameters": [ + "Object", + "__ShakeObject_DisplacementX", + "=", + "(Object.Variable(__ShakeObject_Duration) - Object.ObjectTimerElapsedTime(\"__ShakeObject_DurationTimer\")) / Object.Variable(__ShakeObject_Duration) * Object.Variable(__ShakeObject_PowerX)" + ] + } + ] + }, + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [ + { + "type": { + "value": "VarObjet" + }, + "parameters": [ + "Object", + "__ShakeObject_PowerY", + "!=", + "0" + ] + } + ], + "actions": [ + { + "type": { + "value": "ModVarObjet" + }, + "parameters": [ + "Object", + "__ShakeObject_DisplacementY", + "=", + "(Object.Variable(__ShakeObject_Duration) - Object.ObjectTimerElapsedTime(\"__ShakeObject_DurationTimer\")) / Object.Variable(__ShakeObject_Duration) * Object.Variable(__ShakeObject_PowerY)" + ] + } + ] + } + ] + }, + { + "type": "BuiltinCommonInstructions::Comment", + "color": { + "b": 109, + "g": 230, + "r": 255, + "textB": 0, + "textG": 0, + "textR": 0 + }, + "comment": "After initial shake pick a random direction" + }, + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [ + { + "type": { + "value": "VarObjet" + }, + "parameters": [ + "Object", + "__ShakeObject_InitialShake", + "=", + "0" + ] + } + ], + "actions": [], + "events": [ + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [ + { + "type": { + "value": "VarObjet" + }, + "parameters": [ + "Object", + "__ShakeObject_PowerX", + "!=", + "0" + ] + } + ], + "actions": [ + { + "type": { + "value": "ModVarObjet" + }, + "parameters": [ + "Object", + "__ShakeObject_DisplacementX", + "=", + "(Object.Variable(__ShakeObject_Duration) - Object.ObjectTimerElapsedTime(\"__ShakeObject_DurationTimer\")) / Object.Variable(__ShakeObject_Duration) * Object.Variable(__ShakeObject_PowerX) * RandomWithStep(-1, 1, 2)" + ] + } + ] + }, + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [ + { + "type": { + "value": "VarObjet" + }, + "parameters": [ + "Object", + "__ShakeObject_PowerY", + "!=", + "0" + ] + } + ], + "actions": [ + { + "type": { + "value": "ModVarObjet" + }, + "parameters": [ + "Object", + "__ShakeObject_DisplacementY", + "=", + "(Object.Variable(__ShakeObject_Duration) - Object.ObjectTimerElapsedTime(\"__ShakeObject_DurationTimer\")) / Object.Variable(__ShakeObject_Duration) * Object.Variable(__ShakeObject_PowerY) * RandomWithStep(-1, 1, 2)" + ] + } + ] + } + ] + } + ] + }, + { + "type": "BuiltinCommonInstructions::Comment", + "color": { + "b": 109, + "g": 230, + "r": 255, + "textB": 0, + "textG": 0, + "textR": 0 + }, + "comment": "Calculate Rotation (angle) shake " + }, + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [ + { + "type": { + "value": "VarObjet" + }, + "parameters": [ + "Object", + "__ShakeObject_PowerAngle", + "!=", + "0" + ] + } + ], + "actions": [], + "events": [ + { + "type": "BuiltinCommonInstructions::Comment", + "color": { + "b": 109, + "g": 230, + "r": 255, + "textB": 0, + "textG": 0, + "textR": 0 + }, + "comment": "Every \"even\" shake, rotate clockwise" + }, + { + "type": "BuiltinCommonInstructions::Comment", + "color": { + "b": 109, + "g": 230, + "r": 255, + "textB": 0, + "textG": 0, + "textR": 0 + }, + "comment": "AngleDisplacement = (DesiredDuration - RunningTimer) / DesiredDuration * Amplitude" + }, + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [ + { + "type": { + "value": "BuiltinCommonInstructions::CompareNumbers" + }, + "parameters": [ + "mod(Object.Variable(__ShakeObject_ShakeCounter),2)", + "=", + "0" + ] + } + ], + "actions": [ + { + "type": { + "value": "ModVarObjet" + }, + "parameters": [ + "Object", + "__ShakeObject_DisplacementAngle", + "=", + "-1 * (Object.Variable(__ShakeObject_Duration) - Object.ObjectTimerElapsedTime(\"__ShakeObject_DurationTimer\")) / Object.Variable(__ShakeObject_Duration) * Object.Variable(__ShakeObject_PowerAngle)" + ] + } + ] + }, + { + "type": "BuiltinCommonInstructions::Comment", + "color": { + "b": 109, + "g": 230, + "r": 255, + "textB": 0, + "textG": 0, + "textR": 0 + }, + "comment": "Every \"odd\" shake, rotate counter-clockwise" + }, + { + "type": "BuiltinCommonInstructions::Comment", + "color": { + "b": 109, + "g": 230, + "r": 255, + "textB": 0, + "textG": 0, + "textR": 0 + }, + "comment": "AngleDisplacement = (DesiredDuration - RunningTimer) / DesiredDuration * Amplitude" + }, + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [ + { + "type": { + "value": "BuiltinCommonInstructions::CompareNumbers" + }, + "parameters": [ + "mod(Object.Variable(__ShakeObject_ShakeCounter),2)", + "=", + "1" + ] + } + ], + "actions": [ + { + "type": { + "value": "ModVarObjet" + }, + "parameters": [ + "Object", + "__ShakeObject_DisplacementAngle", + "=", + "(Object.Variable(__ShakeObject_Duration) - Object.ObjectTimerElapsedTime(\"__ShakeObject_DurationTimer\")) / Object.Variable(__ShakeObject_Duration) * Object.Variable(__ShakeObject_PowerAngle)" + ] + } + ] + } + ] + }, + { + "type": "BuiltinCommonInstructions::Comment", + "color": { + "b": 109, + "g": 230, + "r": 255, + "textB": 0, + "textG": 0, + "textR": 0 + }, + "comment": "Calculate Scale shake" + }, + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [ + { + "type": { + "value": "VarObjet" + }, + "parameters": [ + "Object", + "__ShakeObject_PowerScale", + "!=", + "0" + ] + } + ], + "actions": [], + "events": [ + { + "type": "BuiltinCommonInstructions::Comment", + "color": { + "b": 109, + "g": 230, + "r": 255, + "textB": 0, + "textG": 0, + "textR": 0 + }, + "comment": "Calculate object scale displacement, with linear decay over time" + }, + { + "type": "BuiltinCommonInstructions::Comment", + "color": { + "b": 109, + "g": 230, + "r": 255, + "textB": 0, + "textG": 0, + "textR": 0 + }, + "comment": "ScaleDisplacement = (DesiredDuration - RunningTimer) / DesiredDuration * Amplitude * 1/100" + }, + { + "type": "BuiltinCommonInstructions::Comment", + "color": { + "b": 109, + "g": 230, + "r": 255, + "textB": 0, + "textG": 0, + "textR": 0 + }, + "comment": "Every even shake, increase scale" + }, + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [ + { + "type": { + "value": "BuiltinCommonInstructions::CompareNumbers" + }, + "parameters": [ + "mod(Object.Variable(__ShakeObject_ShakeCounter),2)", + "=", + "0" + ] + } + ], + "actions": [ + { + "type": { + "value": "ModVarObjet" + }, + "parameters": [ + "Object", + "__ShakeObject_DisplacementScale", + "=", + "-1 * (Object.Variable(__ShakeObject_Duration) - Object.ObjectTimerElapsedTime(\"__ShakeObject_DurationTimer\")) / Object.Variable(__ShakeObject_Duration) * Object.Variable(__ShakeObject_PowerScale) * (1/100)" + ] + } + ] + }, + { + "type": "BuiltinCommonInstructions::Comment", + "color": { + "b": 109, + "g": 230, + "r": 255, + "textB": 0, + "textG": 0, + "textR": 0 + }, + "comment": "Every \"odd\" shake, decrease scale" + }, + { + "type": "BuiltinCommonInstructions::Comment", + "color": { + "b": 109, + "g": 230, + "r": 255, + "textB": 0, + "textG": 0, + "textR": 0 + }, + "comment": "ScaleDisplacement = -1 * (DesiredDuration - RunningTimer) / DesiredDuration * Amplitude * 1/100" + }, + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [ + { + "type": { + "value": "BuiltinCommonInstructions::CompareNumbers" + }, + "parameters": [ + "mod(Object.Variable(__ShakeObject_ShakeCounter),2)", + "=", + "1" + ] + } + ], + "actions": [ + { + "type": { + "value": "ModVarObjet" + }, + "parameters": [ + "Object", + "__ShakeObject_DisplacementScale", + "=", + "(Object.Variable(__ShakeObject_Duration) - Object.ObjectTimerElapsedTime(\"__ShakeObject_DurationTimer\")) / Object.Variable(__ShakeObject_Duration) * Object.Variable(__ShakeObject_PowerScale) * (1/100)" + ] + } + ] + } + ] + }, + { + "type": "BuiltinCommonInstructions::Comment", + "color": { + "b": 109, + "g": 230, + "r": 255, + "textB": 0, + "textG": 0, + "textR": 0 + }, + "comment": "Save that initial shake has been processed" + }, + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [], + "actions": [ + { + "type": { + "value": "ModVarObjet" + }, + "parameters": [ + "Object", + "__ShakeObject_InitialShake", + "=", + "0" + ] + } + ] + } + ] + } + ], + "parameters": [] + }, + { + "colorB": 228, + "colorG": 176, + "colorR": 74, + "creationTime": 0, + "name": "Move object", + "source": "", + "type": "BuiltinCommonInstructions::Group", + "events": [ + { + "type": "BuiltinCommonInstructions::Comment", + "color": { + "b": 109, + "g": 230, + "r": 255, + "textB": 0, + "textG": 0, + "textR": 0 + }, + "comment": "Calculate the fraction of shake that occured during this frame" + }, + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [], + "actions": [ + { + "type": { + "value": "ModVarObjet" + }, + "parameters": [ + "Object", + "__ShakeObject_PercentTimeElapsedThisFrame", + "=", + "min(1,TimeDelta()/Object.Variable(__ShakeObject_TimeBetweenShakes))" + ] + } + ] + }, + { + "type": "BuiltinCommonInstructions::Comment", + "color": { + "b": 109, + "g": 230, + "r": 255, + "textB": 0, + "textG": 0, + "textR": 0 + }, + "comment": "Increase change for the first half of the shake (move away from original values)" + }, + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [ + { + "type": { + "inverted": true, + "value": "ObjectTimer" + }, + "parameters": [ + "Object", + "\"__ShakeObject_ShakeTimer\"", + "Object.Variable(__ShakeObject_TimeBetweenShakes)/2" + ] + } + ], + "actions": [], + "events": [ + { + "type": "BuiltinCommonInstructions::Comment", + "color": { + "b": 109, + "g": 230, + "r": 255, + "textB": 0, + "textG": 0, + "textR": 0 + }, + "comment": "Change position" + }, + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [ + { + "type": { + "value": "BuiltinCommonInstructions::Or" + }, + "parameters": [], + "subInstructions": [ + { + "type": { + "value": "VarObjet" + }, + "parameters": [ + "Object", + "__ShakeObject_PowerX", + "!=", + "0" + ] + }, + { + "type": { + "value": "VarObjet" + }, + "parameters": [ + "Object", + "__ShakeObject_PowerY", + "!=", + "0" + ] + } + ] + } + ], + "actions": [ + { + "type": { + "value": "SetX" + }, + "parameters": [ + "Object", + "+", + "round(1024 * Object.Variable(__ShakeObject_DisplacementX) * Object.Variable(__ShakeObject_PercentTimeElapsedThisFrame)) / 1024" + ] + }, + { + "type": { + "value": "SetY" + }, + "parameters": [ + "Object", + "+", + "round(1024 * Object.Variable(__ShakeObject_DisplacementY) * Object.Variable(__ShakeObject_PercentTimeElapsedThisFrame)) / 1024" + ] + } + ], + "events": [ + { + "type": "BuiltinCommonInstructions::Comment", + "color": { + "b": 109, + "g": 230, + "r": 255, + "textB": 0, + "textG": 0, + "textR": 0 + }, + "comment": "Save movement to calculate drift" + }, + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [], + "actions": [ + { + "type": { + "value": "ModVarObjet" + }, + "parameters": [ + "Object", + "__ShakeObject_DisplacementTravelledX", + "+", + "round(1024 * Object.Variable(__ShakeObject_DisplacementX) * Object.Variable(__ShakeObject_PercentTimeElapsedThisFrame)) / 1024" + ] + }, + { + "type": { + "value": "ModVarObjet" + }, + "parameters": [ + "Object", + "__ShakeObject_DisplacementTravelledY", + "+", + "round(1024 * Object.Variable(__ShakeObject_DisplacementY) * Object.Variable(__ShakeObject_PercentTimeElapsedThisFrame)) / 1024" + ] + } + ] + } + ] + }, + { + "type": "BuiltinCommonInstructions::Comment", + "color": { + "b": 109, + "g": 230, + "r": 255, + "textB": 0, + "textG": 0, + "textR": 0 + }, + "comment": "Change angle" + }, + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [ + { + "type": { + "value": "BuiltinCommonInstructions::CompareNumbers" + }, + "parameters": [ + "Object.Variable(__ShakeObject_PowerAngle)", + "!=", + "0" + ] + } + ], + "actions": [ + { + "type": { + "value": "SetAngle" + }, + "parameters": [ + "Object", + "+", + "round(1024 * Object.Variable(__ShakeObject_DisplacementAngle) * Object.Variable(__ShakeObject_PercentTimeElapsedThisFrame)) / 1024" + ] + } + ], + "events": [ + { + "type": "BuiltinCommonInstructions::Comment", + "color": { + "b": 109, + "g": 230, + "r": 255, + "textB": 0, + "textG": 0, + "textR": 0 + }, + "comment": "Save movement to calculate drift" + }, + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [], + "actions": [ + { + "type": { + "value": "ModVarObjet" + }, + "parameters": [ + "Object", + "__ShakeObject_AngleTravelled", + "+", + "round(1024 * Object.Variable(__ShakeObject_DisplacementAngle) * Object.Variable(__ShakeObject_PercentTimeElapsedThisFrame)) / 1024" + ] + } + ] + } + ] + }, + { + "type": "BuiltinCommonInstructions::Comment", + "color": { + "b": 109, + "g": 230, + "r": 255, + "textB": 0, + "textG": 0, + "textR": 0 + }, + "comment": "Change scale" + }, + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [ + { + "type": { + "value": "BuiltinCommonInstructions::CompareNumbers" + }, + "parameters": [ + "Object.Variable(__ShakeObject_PowerScale)", + "!=", + "0" + ] + } + ], + "actions": [ + { + "type": { + "value": "ScalableCapability::ScalableBehavior::SetValue" + }, + "parameters": [ + "Object", + "Scale", + "+", + "round(1024 * Object.Variable(__ShakeObject_DisplacementScale) * Object.Variable(__ShakeObject_PercentTimeElapsedThisFrame)) / 1024" + ] + } + ], + "events": [ + { + "type": "BuiltinCommonInstructions::Comment", + "color": { + "b": 109, + "g": 230, + "r": 255, + "textB": 0, + "textG": 0, + "textR": 0 + }, + "comment": "Save movement to calculate drift" + }, + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [], + "actions": [ + { + "type": { + "value": "ModVarObjet" + }, + "parameters": [ + "Object", + "__ShakeObject_ScaleTravelled", + "+", + "round(1024 * Object.Variable(__ShakeObject_DisplacementScale) * Object.Variable(__ShakeObject_PercentTimeElapsedThisFrame)) / 1024" + ] + } + ] + } + ] + } + ] + }, + { + "type": "BuiltinCommonInstructions::Comment", + "color": { + "b": 109, + "g": 230, + "r": 255, + "textB": 0, + "textG": 0, + "textR": 0 + }, + "comment": "Decrease change the second half of the shake (return to original position)" + }, + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [ + { + "type": { + "value": "ObjectTimer" + }, + "parameters": [ + "Object", + "\"__ShakeObject_ShakeTimer\"", + "Object.Variable(__ShakeObject_TimeBetweenShakes)/2" + ] + } + ], + "actions": [], + "events": [ + { + "type": "BuiltinCommonInstructions::Comment", + "color": { + "b": 109, + "g": 230, + "r": 255, + "textB": 0, + "textG": 0, + "textR": 0 + }, + "comment": "Change position" + }, + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [ + { + "type": { + "value": "BuiltinCommonInstructions::Or" + }, + "parameters": [], + "subInstructions": [ + { + "type": { + "value": "VarObjet" + }, + "parameters": [ + "Object", + "__ShakeObject_PowerX", + "!=", + "0" + ] + }, + { + "type": { + "value": "VarObjet" + }, + "parameters": [ + "Object", + "__ShakeObject_PowerY", + "!=", + "0" + ] + } + ] + } + ], + "actions": [ + { + "type": { + "value": "SetX" + }, + "parameters": [ + "Object", + "-", + "round(1024 * Object.Variable(__ShakeObject_DisplacementX) * Object.Variable(__ShakeObject_PercentTimeElapsedThisFrame)) / 1024" + ] + }, + { + "type": { + "value": "SetY" + }, + "parameters": [ + "Object", + "-", + "round(1024 * Object.Variable(__ShakeObject_DisplacementY) * Object.Variable(__ShakeObject_PercentTimeElapsedThisFrame)) / 1024" + ] + } + ], + "events": [ + { + "type": "BuiltinCommonInstructions::Comment", + "color": { + "b": 109, + "g": 230, + "r": 255, + "textB": 0, + "textG": 0, + "textR": 0 + }, + "comment": "Save movement to calculate drift" + }, + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [], + "actions": [ + { + "type": { + "value": "ModVarObjet" + }, + "parameters": [ + "Object", + "__ShakeObject_DisplacementTravelledX", + "-", + "round(1024 * Object.Variable(__ShakeObject_DisplacementX) * Object.Variable(__ShakeObject_PercentTimeElapsedThisFrame)) / 1024" + ] + }, + { + "type": { + "value": "ModVarObjet" + }, + "parameters": [ + "Object", + "__ShakeObject_DisplacementTravelledY", + "-", + "round(1024 * Object.Variable(__ShakeObject_DisplacementY) * Object.Variable(__ShakeObject_PercentTimeElapsedThisFrame)) / 1024" + ] + } + ] + } + ] + }, + { + "type": "BuiltinCommonInstructions::Comment", + "color": { + "b": 109, + "g": 230, + "r": 255, + "textB": 0, + "textG": 0, + "textR": 0 + }, + "comment": "Change angle" + }, + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [ + { + "type": { + "value": "BuiltinCommonInstructions::CompareNumbers" + }, + "parameters": [ + "Object.Variable(__ShakeObject_PowerAngle)", + "!=", + "0" + ] + } + ], + "actions": [ + { + "type": { + "value": "SetAngle" + }, + "parameters": [ + "Object", + "-", + "round(1024 * Object.Variable(__ShakeObject_DisplacementAngle) * Object.Variable(__ShakeObject_PercentTimeElapsedThisFrame)) / 1024" + ] + } + ], + "events": [ + { + "type": "BuiltinCommonInstructions::Comment", + "color": { + "b": 109, + "g": 230, + "r": 255, + "textB": 0, + "textG": 0, + "textR": 0 + }, + "comment": "Save movement to calculate drift" + }, + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [], + "actions": [ + { + "type": { + "value": "ModVarObjet" + }, + "parameters": [ + "Object", + "__ShakeObject_AngleTravelled", + "-", + "round(1024 * Object.Variable(__ShakeObject_DisplacementAngle) * Object.Variable(__ShakeObject_PercentTimeElapsedThisFrame)) / 1024" + ] + } + ] + } + ] + }, + { + "type": "BuiltinCommonInstructions::Comment", + "color": { + "b": 109, + "g": 230, + "r": 255, + "textB": 0, + "textG": 0, + "textR": 0 + }, + "comment": "Change scale" + }, + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [ + { + "type": { + "value": "BuiltinCommonInstructions::CompareNumbers" + }, + "parameters": [ + "Object.Variable(__ShakeObject_PowerScale)", + "!=", + "0" + ] + } + ], + "actions": [ + { + "type": { + "value": "ScalableCapability::ScalableBehavior::SetValue" + }, + "parameters": [ + "Object", + "Scale", + "-", + "round(1024 * Object.Variable(__ShakeObject_DisplacementScale) * Object.Variable(__ShakeObject_PercentTimeElapsedThisFrame)) / 1024" + ] + } + ], + "events": [ + { + "type": "BuiltinCommonInstructions::Comment", + "color": { + "b": 109, + "g": 230, + "r": 255, + "textB": 0, + "textG": 0, + "textR": 0 + }, + "comment": "Save movement to calculate drift" + }, + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [], + "actions": [ + { + "type": { + "value": "ModVarObjet" + }, + "parameters": [ + "Object", + "__ShakeObject_ScaleTravelled", + "-", + "round(1024 * Object.Variable(__ShakeObject_DisplacementScale) * Object.Variable(__ShakeObject_PercentTimeElapsedThisFrame)) / 1024" + ] + } + ] + } + ] + } + ] + } + ], + "parameters": [] + } + ] + } + ], + "parameters": [] + }, + { + "colorB": 228, + "colorG": 176, + "colorR": 74, + "creationTime": 0, + "name": "Stop shaking", + "source": "", + "type": "BuiltinCommonInstructions::Group", + "events": [ + { + "type": "BuiltinCommonInstructions::Comment", + "color": { + "b": 109, + "g": 230, + "r": 255, + "textB": 0, + "textG": 0, + "textR": 0 + }, + "comment": "Stop shaking when the duration has been reached (or if the stop shaking function was called)" + }, + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [ + { + "type": { + "value": "BuiltinCommonInstructions::Or" + }, + "parameters": [], + "subInstructions": [ + { + "type": { + "value": "ObjectTimer" + }, + "parameters": [ + "Object", + "\"__ShakeObject_DurationTimer\"", + "Object.Variable(__ShakeObject_Duration)" + ] + }, + { + "type": { + "value": "VarObjet" + }, + "parameters": [ + "Object", + "__ShakeObject_ShakeInProgress", + "=", + "-1" + ] + } + ] + }, + { + "type": { + "value": "BuiltinCommonInstructions::Once" + }, + "parameters": [] + } + ], + "actions": [ + { + "type": { + "value": "ModVarObjet" + }, + "parameters": [ + "Object", + "__ShakeObject_ShakeInProgress", + "=", + "0" + ] + }, + { + "type": { + "value": "ModVarObjet" + }, + "parameters": [ + "Object", + "__ShakeObject_ShakeCounter", + "=", + "0" + ] + } + ], + "events": [ + { + "colorB": 228, + "colorG": 176, + "colorR": 74, + "creationTime": 0, + "name": "Correct for drift and reset drift tracking variables", + "source": "", + "type": "BuiltinCommonInstructions::Group", + "events": [ + { + "type": "BuiltinCommonInstructions::Comment", + "color": { + "b": 109, + "g": 230, + "r": 255, + "textB": 0, + "textG": 0, + "textR": 0 + }, + "comment": "Move to correct any drift from previous shake" + }, + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [ + { + "type": { + "value": "BuiltinCommonInstructions::Or" + }, + "parameters": [], + "subInstructions": [ + { + "type": { + "value": "VarObjet" + }, + "parameters": [ + "Object", + "__ShakeObject_PowerX", + "!=", + "0" + ] + }, + { + "type": { + "value": "VarObjet" + }, + "parameters": [ + "Object", + "__ShakeObject_PowerY", + "!=", + "0" + ] + } + ] + } + ], + "actions": [ + { + "type": { + "value": "SetX" + }, + "parameters": [ + "Object", + "-", + "Object.Variable(__ShakeObject_DisplacementTravelledX)" + ] + }, + { + "type": { + "value": "SetY" + }, + "parameters": [ + "Object", + "-", + "Object.Variable(__ShakeObject_DisplacementTravelledY)" + ] + } + ] + }, + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [ + { + "type": { + "value": "VarObjet" + }, + "parameters": [ + "Object", + "__ShakeObject_PowerAngle", + "!=", + "0" + ] + } + ], + "actions": [ + { + "type": { + "value": "SetAngle" + }, + "parameters": [ + "Object", + "-", + "Object.Variable(__ShakeObject_AngleTravelled)" + ] + } + ] + }, + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [ + { + "type": { + "value": "VarObjet" + }, + "parameters": [ + "Object", + "__ShakeObject_PowerScale", + "!=", + "0" + ] + } + ], + "actions": [ + { + "type": { + "value": "ScalableCapability::ScalableBehavior::SetValue" + }, + "parameters": [ + "Object", + "Scale", + "-", + "Object.Variable(__ShakeObject_ScaleTravelled)" + ] + } + ] + }, + { + "type": "BuiltinCommonInstructions::Comment", + "color": { + "b": 109, + "g": 230, + "r": 255, + "textB": 0, + "textG": 0, + "textR": 0 + }, + "comment": "Reset drift detection variables" + }, + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [], + "actions": [ + { + "type": { + "value": "ModVarObjet" + }, + "parameters": [ + "Object", + "__ShakeObject_DisplacementTravelledX", + "=", + "0" + ] + }, + { + "type": { + "value": "ModVarObjet" + }, + "parameters": [ + "Object", + "__ShakeObject_DisplacementTravelledY", + "=", + "0" + ] + }, + { + "type": { + "value": "ModVarObjet" + }, + "parameters": [ + "Object", + "__ShakeObject_AngleTravelled", + "=", + "0" + ] + }, + { + "type": { + "value": "ModVarObjet" + }, + "parameters": [ + "Object", + "__ShakeObject_ScaleTravelled", + "=", + "0" + ] + } + ] + } + ], + "parameters": [] + } + ] + } + ], + "parameters": [] + } + ], + "parameters": [] + } + ], + "parameters": [ + { + "description": "Object", + "name": "Object", + "type": "object" + }, + { + "description": "Behavior", + "name": "Behavior", + "supplementaryInformation": "ShakeObject::ShakeObject_PositionAngleScale", + "type": "behavior" + } + ], + "objectGroups": [] + }, + { + "description": "Stop shaking an object.", + "fullName": "Stop shaking an object", + "functionType": "Action", + "name": "StopShaking", + "sentence": "Stop shaking _PARAM0_", + "events": [ + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [ + { + "type": { + "value": "ShakeObject::ShakeObject_PositionAngleScale::IsShaking" + }, + "parameters": [ + "Object", + "Behavior", + "" + ] + } + ], + "actions": [ + { + "type": { + "value": "ModVarObjet" + }, + "parameters": [ + "Object", + "__ShakeObject_ShakeInProgress", + "=", + "-1" + ] + } + ] + } + ], + "parameters": [ + { + "description": "Object", + "name": "Object", + "type": "object" + }, + { + "description": "Behavior", + "name": "Behavior", + "supplementaryInformation": "ShakeObject::ShakeObject_PositionAngleScale", + "type": "behavior" + } + ], + "objectGroups": [] + }, + { + "description": "Check if an object is shaking.", + "fullName": "Check if an object is shaking", + "functionType": "Condition", + "name": "IsShaking", + "sentence": "_PARAM0_ is shaking", + "events": [ + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [ + { + "type": { + "value": "VarObjet" + }, + "parameters": [ + "Object", + "__ShakeObject_ShakeInProgress", + "=", + "1" + ] + } + ], + "actions": [ + { + "type": { + "value": "SetReturnBoolean" + }, + "parameters": [ + "True" + ] + } + ] + } + ], + "parameters": [ + { + "description": "Object", + "name": "Object", + "type": "object" + }, + { + "description": "Behavior", + "name": "Behavior", + "supplementaryInformation": "ShakeObject::ShakeObject_PositionAngleScale", + "type": "behavior" + } + ], + "objectGroups": [] + } + ], + "eventsFunctionsFolderStructure": { + "folderName": "__ROOT", + "children": [ + { + "functionName": "ShakeObject_PositionAngleScale" + }, + { + "functionName": "doStepPostEvents" + }, + { + "functionName": "StopShaking" + }, + { + "functionName": "IsShaking" + } + ] + }, + "propertyDescriptors": [ + { + "value": "", + "type": "Behavior", + "label": "Scalable capability", + "extraInformation": [ + "ScalableCapability::ScalableBehavior" + ], + "choices": [], + "name": "Scale" + } + ], + "propertiesFolderStructure": { + "folderName": "__ROOT", + "children": [ + { + "propertyName": "Scale" + } + ] + } + } + ], + "eventsBasedObjects": [] } ], "externalLayouts": [] diff --git a/examples/starting-3D-platformer/assets/Flat dark joystick border.png b/examples/starting-3D-platformer/assets/Flat dark joystick border.png index 606bf85b223d68dbdd1be4f5ac851f2018e13828..b4e83e112eb9375cefbb86a6d044a972f8bf4dcf 100644 GIT binary patch literal 3751 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 delta 928 zcmV;R17G~79jgbB8Gi!+006uZmmdHC02@$DR7Ff_aWyqHHa0djH8nOiHZ3hJHa0dm zI5|R^?&H|1(F!EEFDZlRnwi_ z{vfi zj=HzjIAJ?^*aIR1yr3o5f0`Hd%5^<6F6Up zP~Fjl;*KVq#|bI9VKHMl8{r`)iPZ%{-xG$jf4=?q~^F0)IA24NSmi2{R2agHyOAVdeoQ zxPbQ(79!wkIWZ#NI!%xsUi&yP0Snv1t6i5;0fp`1zdpu#*d3k2|9q@mKxK#E_j(Kt z!LJ|`GN7)T^niore1Z;j$NmwypId1iKp=wpMU(OX1$5-Pn-+2DQ3 z3U8HfoorJ0VFQ$enSS6!7WRpkSLewMoFS+@5$ws+?STP?b}+=r0-O&JIbb}yTqpDq z_kD-lXD%rF#snxfL8d2)U`M4yN4i*I^UWcQjl?nfK%C*3a$s;&f%6R|JUAMx!11?+{;EmT?0T5AdBGkQj7xl&{aQ(}c)V~^X*e%3^Jziw;PzI z-Q)Pxrd*^ diff --git a/examples/starting-3D-platformer/assets/Flat dark joystick thumb.png b/examples/starting-3D-platformer/assets/Flat dark joystick thumb.png index acd005cc067032996264e510b58c8dbc0c102b62..8acf179f8660be36ccea1198105da35c07154fd7 100644 GIT binary patch literal 2939 zcmV->3xxEEP)IW485iPkW{Hp)}3kXR> zK~#9!?Oj=H8`l**_stBok)rm+alOTHY)jTcOO_Pdau+1&5)^3yq%HEtw|uhZ1*opyTSlNpA;d31v<-mnz&SAh32>in$=8J7LtSkrZm$^ucGWa^ z6!RueX69Q&R0kk4#epydaJxA@{SzY5+uL>0ye|c)so$qD^Rt?!y#%6%(#ihjpa33} zwtSU{|Jv1dLf$t5RM$Q7jHcj&qo=AHvH zcS_s(QP1g?WA~H*Teejd`1Ae0AQI2xJ^(YnIXQgmg^QO4CevPk+J^mmiNyN=@{&o4 zVR(>{nF#=xot=SYCOvlsnbnh>CyyRZO986tn{)y3TcPRC1JIJzWrHBEy*uVBgT{=5QcnwD$<2%zoR zxp}=+b&ZuN6`;C)U$gGXI0B%=iDW1=4MHq^%0dWen(kC?F4y&pqt$i$8dD-bZNvU= zX`1#vfc&(oSinvmE>F|7LxINTCma78=>)(5BZ0rEo-~mj}0bVfbBa1dD(v70D$6j@OmKx zgrIsyW}V4A}ur1`-h5}JruV9n#c1v5q;6+E@4)*S1PWL z%YrIv8+DJz(+Q$Vm%0RkxzDn!n%<5UJ8BOQLNtJ=!euPs-C!qx#;6vwqavW`y8af@ zZ_dtrA%SifuWhdgXiEy9XJl*u(H57hq~(G@me*Unv;ew(5P-*}Drqp7Xs{p(%Pf0~&CK7p|zqdWKpaih*^GR zUH~Dque+Q$3qjg9?+}0*TuL170z&KoAZS;hvEJ}xoN_5~9;RsqgsvOz&_k*&i**6BnP*W7-?<3WC5F?_yU*p`Lh_$VeO zLg4h8mF9dkLj-?b0rCn8p=lZ-;Yo}R55cmmc)nXgwGGX;i74unL$5CvW#tcmKDRi+ zG)-I`{20t?&62-i^Hz8~3rFx-Dcrhw9pTA|SQa!PD8_0=B0}jRvUAq%?SpLZ((6-%5XkYyTE9X@%+XK7 z6a5C6t{cf-8k(jB48t&tRdF{z6cG)v7C>8;1){Y#jz>hWZEIEBeJTKR)L)5&C!@~4 zkAx?&RuPFrmfWV4!sJ9KRzI=?12fTfky2vl=JiEIFwI#E58Yg|0^GiJ1Lh)8u9U*i z&1*5OnHfPHzz|}rb_`EWU}k0-Ma8Aa@%kX8L~v{rq2Sn>^#41{&8wF`MsBVjdHIFV z4Fi*t6Br*Ij`!IWA)T3TQykCnu`LU?Zd{wY+H>#S+$lmRIF3+oeA(PE^N;}Zjb)}E zm-R$(!+b>$QJYI#%1Wt0!N4__iIm}tur2eP%R_2lTh=84fI#B`cZ@j&`Maa_u$Y^c zqTD>-FqBfK=7%Dblpnd2I4;0D^8%12K7f0x%TfCwK_}(~7#|z!0bshkI48h#DBL?Q zz}1WWQ%b4BE-%hNO8LRS`L2n10RTV{{miu?XQE#S@$-4!Kl@`U93BKPOm0sw$is)d=)x_l)%__?2zYWe)< zcL{K|`;BCp8ZFJ5c^$f{XzIlF}*ihfT{o(A{=yRkKM}UB8{_Xzd9F$F@0f z4?#+aU~qiN?ZNReNCyO9=8nmU;6rCRT6{tZx4-!*1W$G`LoZ*uIxw90 z!Xk;N3_Q}H>)M+_h^l*XOh>6MY1=>S?YPI@DSy`x#_pyc=(_O&-6KzCXXZ zv-R)EFRdNIuDT|TB|m3)JU=C(O-R4xmdwl_o9666A+$gBo<8A7J?l6L+be1_gl??& zWMuqhu42kcCFuZWm{li0Pfmp=C(ieE&N_JlDUHI)+NLt0(OymHuaKY}b7e6(K)Ph+ z3(~e*zRiP*%l>rgtnHC$4;H4S$509AMTK!o)q_;4Vv8 zq-%bJcA5O|JulJAeuQX3P>IsnfaZ6Kar2%-@G*)QG=HZE&goRnsb>)#yPBG1sB!Hh z43d+OIs3N_GYkgUzhtn9^S>bspW_gq{VNwViuO^0PYl=T0VPPUzF*M(>6^wL0BHZ@ zvWDhTyoJGYF@JnE6Uejc!3TG#~e;_b+x__nEUko!5E+){gO+8MW_ zw!W>|D~;a);deGMLT(Z&UC$fvvaWY6#qkPCAl3O6NYLvOv|0Q7ArXJfHO(NbN$JSE zv_5Q9<57-dJ9nTwHg>n+?}Hn0XP~q-FhZf#^$lz_!DVVSlew99XbM^Q7a!of` z6c|_<7z7xY92htl7^%&v<4&;gusm2@{ORAU8sRj@3RZ!~l}t-y7ux-M9Q=Lyk&lm` vJ^35QxQ~fe4qyTK?LW(eijrS&N2u&%kUeT7qT^bX$^ZnOu6{1-oD!M`H&=dpzqpF diff --git a/examples/starting-3D-platformer/assets/StarterToggle_Keyboard.png b/examples/starting-3D-platformer/assets/StarterToggle_Keyboard.png index 54eb525bd31884a3c9174fd55987b5d12d415c2b..aabe87250073b2858bdb31fa561d3cd73a5c2456 100644 GIT binary patch literal 3413 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 delta 1267 zcmVk0APj~}1qIg|y;$4-|3eSdR=EXR#Ljv0&8i#El0bNq zCCPtbOc?zjLt;Ha9GISu$7A+MCe}mfMjVETgcv#JMftk%V+^MpZhzV1`Lv#fQTY|# zmVH%Kc?7GE@qgm|MuXXE5Wx|LA|lGXymX>j0mHBaiOCTeWuiGqm?tdkJb~nKEi%ds zbM!h!xMXZ&gS-Jn!ieZO!|zk!l0H6(3xx%l_7e^=V*nBnTyJoR3x(m7Y_~MkS+K|$ z43KnXp6`KzQc6$TjZ&Q@kr{*CB>m&JS3P*(6`NzwM1R5L6lTO&ICHWPon+|X`e7_z zMLC6F0kUhTIljIC`n@PVwP`u5k8%6kln44E_qKxh( zb{b06LXp^Yg%^cvtZ<1NCHc|QMWFQ-3U_9ArK2>SoN9MLNe(uuyLk@p)7%> zJlGHAXO1vp$Jbv&St9IU$JcM8FoX~@Q3#uw_NohXG+4jH|n z0sTU zpp0)pVW8-j`U)3UIhi+}#$BQ8($xh<8D1zSF{H#gGk+;3T}A$ClUFXto{2|3#opr< z1{e9RtJiwXxPn1_o!Y>j4vgs*Rnv1zdY19I z1T&QU(iw}qhn0M_M07d@(=B?wrnEYRR?F(SFg8$}3iw!f8+gbg+55IH7m6mdx_{;G zwJ5GVf)}rJh!K6BOpyHTY)Y&~5puDyja~ZueJI8j_G9+&*0hR%T$yseRbcqgywvf)bNZ*(}g~01Yys?kkB1~sKZ8{!H`|Y zbwYLk4a7gL^N~YT3{4p$1wFQ5i0U&(+omc9F}7{A?VAP;ziABgtA=yGYGnGW2HC&s d-wXbp{R5(SZUFku=BNMw002ovPDHLkV1n@6OJ4v0 diff --git a/examples/starting-3D-platformer/assets/StarterToggle_Touch.png b/examples/starting-3D-platformer/assets/StarterToggle_Touch.png index b38bab4da6aa9be1bf4487046f521efd39f403b6..204462798cece3a787bf29aaf3c77d2867382756 100644 GIT binary patch literal 3329 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 diff --git a/examples/starting-3D-platformer/assets/StartingGround.png b/examples/starting-3D-platformer/assets/StartingGround.png index 1c66f93412b0f53e8c73213298b347157759734a..edec42fec83b5ace1e1abd51c617a430c9a319be 100644 GIT binary patch literal 2824 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 delta 4495 zcmY+Ic{~%~|G?35%S|dr48@!ssF*Z4Gr2NX5{9`qWXYH#rOT0X4AGpCGvv$?W6Erf zzKF0`XpXX4{MPrM-|zRw^Lnn&^L$>P*Yn5w{d&@5N>rZ7>PPeQa&d9p{6Krh=>)

CVi05h7CM;*%6n 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 literal 378 zcmeAS@N?(olHy`uVBq!ia0vp^4Is?H1SEZ8zRdwrY)RhkE({(F?hNicYo9j)MK}vQ zB8wRqxP?KOkzv*x37{Z*iKnkC`ztmf9$D`E|670}jO?B+jv*Dd-rn8F*=iut7C2Q$ zP`v9mf8wv||V~00!*A{PeEu8xM;`w=bRd-)koZnsVy{m5C+^YZ2 z1Uh(uCNVKMFfeccO=M7DU|<0`QGhJLAMywO=-=p++8q2|e`Wn)>BE9c@^{)RRP6uF v-1ObLsfL*vmO$Mo$MBAyl|q5$ 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 FV1j1w)3N{n delta 627 zcmV-(0*w8$9DxOp8Gi!+006nq0-pc?05(ueR7Ff_aWyqHHa0dmI5;&mH8L_XHa0dg zGBP$cHa0dk|NsAAUtjX_^4{Lw#l^)&Mn->se~*ukx&LcZ0000AbW%=J0B>IZjxp~R zy4HO*rZ@lq010qNS#tmY3labT3lag+-G2N400G-cL_t(|+JD8F>ypAC4227X5~x;v z|ChDy&bnGlZe(`P@6Ii$Vi%H32h?z{` zA&({os!ck)jd&rN1@*kv&X;~w(7+>zIGK0^u?M`ITYobVd%(4ET6Mt2>z{${OJ<6^ zsnagq<8`6Sd|rA-EH^URbgQvHQ^O|E%x{@$LOs$9eVLZ5Rh; z*XQ!VgKQZc-=f2o!?Ed*2lmdfbz>*?te|XMb>FPA<&m<}EJWI#E9%{YoXZKS(#-Mi zEz^go>*bgJ1}U?hv%<;dkt!VPW>+x%4v0{XX>l^EM0ovUQ4t~&+a0mURTHt&FNy#F N002ovPDHLkV1nr&BUJzZ diff --git a/examples/starting-3D-platformer/starting-3D-platformer.json b/examples/starting-3D-platformer/starting-3D-platformer.json index a9d2e8d78..475147d38 100644 --- a/examples/starting-3D-platformer/starting-3D-platformer.json +++ b/examples/starting-3D-platformer/starting-3D-platformer.json @@ -1,7 +1,7 @@ { "firstLayout": "", "gdVersion": { - "build": 272, + "build": 274, "major": 5, "minor": 6, "revision": 0 @@ -290,6 +290,12 @@ "persistentUuid": "690e89c7-0fce-446f-9764-7d695c2d8f98", "type": "boolean", "value": false + }, + { + "name": "CoinPitch", + "persistentUuid": "61f41e22-a769-42d4-8287-dda386904970", + "type": "number", + "value": 1 } ], "instances": [ @@ -597,13 +603,13 @@ "angle": 0, "customSize": true, "depth": 0, - "height": 120, + "height": 104, "keepRatio": true, "layer": "MobileControls", "locked": true, "name": "JumpButton", "persistentUuid": "c641f19e-29c5-4424-be1c-656b5505f1ac", - "width": 120, + "width": 104, "x": 1104, "y": 576, "zOrder": 8, @@ -709,6 +715,114 @@ "numberProperties": [], "stringProperties": [], "initialVariables": [] + }, + { + "angle": 0, + "customSize": false, + "height": 0, + "keepRatio": true, + "layer": "", + "name": "Coin", + "persistentUuid": "310271a3-6f7b-4285-b03a-a1517a67dd52", + "rotationY": 90, + "width": 0, + "x": 819, + "y": 208, + "z": 30, + "zOrder": 4, + "numberProperties": [], + "stringProperties": [], + "initialVariables": [] + }, + { + "angle": 0, + "customSize": false, + "height": 0, + "keepRatio": true, + "layer": "", + "name": "Coin", + "persistentUuid": "bb2c5086-e6a4-4605-a447-bc5359002f77", + "rotationY": 90, + "width": 0, + "x": 819, + "y": 144, + "z": 30, + "zOrder": 4, + "numberProperties": [], + "stringProperties": [], + "initialVariables": [] + }, + { + "angle": 0, + "customSize": false, + "height": 0, + "keepRatio": true, + "layer": "", + "name": "Coin", + "persistentUuid": "e6cfcfc4-078c-4afa-87dd-f759b8cdb055", + "rotationY": 90, + "width": 0, + "x": 819, + "y": 80, + "z": 30, + "zOrder": 4, + "numberProperties": [], + "stringProperties": [], + "initialVariables": [] + }, + { + "angle": 0, + "customSize": false, + "height": 0, + "keepRatio": true, + "layer": "", + "name": "Coin", + "persistentUuid": "511ebaac-547c-41ab-9b7f-f9a46be6d2ac", + "rotationY": 90, + "width": 0, + "x": 461, + "y": 208, + "z": 30, + "zOrder": 4, + "numberProperties": [], + "stringProperties": [], + "initialVariables": [] + }, + { + "angle": 0, + "customSize": false, + "height": 0, + "keepRatio": true, + "layer": "", + "name": "Coin", + "persistentUuid": "0ee32a7d-4743-4e39-ada1-b246e32ecfe2", + "rotationY": 90, + "width": 0, + "x": 461, + "y": 144, + "z": 30, + "zOrder": 4, + "numberProperties": [], + "stringProperties": [], + "initialVariables": [] + }, + { + "angle": 0, + "customSize": false, + "height": 0, + "keepRatio": true, + "layer": "", + "name": "Coin", + "persistentUuid": "e2c40bb4-8aac-4276-a850-cd9cef385de9", + "rotationY": 90, + "width": 0, + "x": 461, + "y": 80, + "z": 30, + "zOrder": 4, + "numberProperties": [], + "stringProperties": [], + "initialVariables": [] } ], "objects": [ @@ -1147,11 +1261,37 @@ "adaptCollisionMaskAutomatically": true, "assetStoreId": "9c727020616afdd6ba786b8af206a90481f07db0ca175ed6a4cc5b7e01c66d06", "name": "JumpButton", + "persistentUuid": "e29ad2f3-ceef-4d9c-8de7-903abac2b4b0", "type": "Sprite", "updateIfNotVisible": false, "variables": [], "effects": [], "behaviors": [ + { + "name": "ButtonFSM", + "type": "ButtonStates::ButtonFSM", + "ShouldCheckHovering": true, + "State": "Idle", + "TouchId": 0, + "TouchIsInside": false, + "MouseIsInside": false, + "Index": 0 + }, + { + "name": "ButtonScaleTween", + "type": "ButtonStates::ButtonScaleTween", + "Scale": "Scale", + "ButtonFSM": "ButtonFSM", + "Tween": "Tween", + "IdleScale": 1.3, + "FocusedScale": 1.25, + "FadeInDuration": 0.02, + "FadeOutDuration": 0.05, + "PressedScale": 1.2, + "FadeInEasing": "easeOutQuad", + "FadeOutEasing": "easeInQuad", + "PreviousState": "Idle" + }, { "name": "MultitouchButton", "type": "SpriteMultitouchJoystick::MultitouchButton", @@ -1160,6 +1300,10 @@ "TouchId": 0, "TouchIndex": 2.0247e-320, "IsReleased": false + }, + { + "name": "Tween", + "type": "Tween::TweenBehavior" } ], "animations": [ @@ -1263,61 +1407,130 @@ }, "events": [ { - "type": "BuiltinCommonInstructions::Standard", - "conditions": [ - { - "type": { - "value": "Collision3D::AreWithinDistance" - }, - "parameters": [ - "", - "Coin", - "Player", - "50", - "", - "" - ] - } - ], - "actions": [ + "colorB": 228, + "colorG": 176, + "colorR": 74, + "creationTime": 0, + "folded": true, + "name": "Coins", + "source": "", + "type": "BuiltinCommonInstructions::Group", + "events": [ { - "type": { - "value": "Delete" - }, - "parameters": [ - "Coin", - "" + "type": "BuiltinCommonInstructions::Standard", + "conditions": [ + { + "type": { + "value": "CompareTimer" + }, + "parameters": [ + "", + "\"Coin_PitchTimer\"", + ">", + "1" + ] + } + ], + "actions": [ + { + "type": { + "value": "SetNumberVariable" + }, + "parameters": [ + "CoinPitch", + "=", + "RandomFloatInRange(0.9,1.1)" + ] + }, + { + "type": { + "value": "RemoveTimer" + }, + "parameters": [ + "", + "\"Coin_PitchTimer\"" + ] + } ] }, { - "type": { - "value": "PlaySound" - }, - "parameters": [ - "", - "assets/CoinPickUp.wav", - "", - "100", - "RandomFloatInRange(0.9,1.1)" + "type": "BuiltinCommonInstructions::Standard", + "conditions": [ + { + "type": { + "value": "Collision3D::AreWithinDistance" + }, + "parameters": [ + "", + "Coin", + "Player", + "50", + "", + "" + ] + } + ], + "actions": [ + { + "type": { + "value": "Delete" + }, + "parameters": [ + "Coin", + "" + ] + }, + { + "type": { + "value": "PlaySound" + }, + "parameters": [ + "", + "assets/CoinPickUp.wav", + "", + "100", + "CoinPitch" + ] + }, + { + "type": { + "value": "ResetTimer" + }, + "parameters": [ + "", + "\"Coin_PitchTimer\"" + ] + }, + { + "type": { + "value": "SetNumberVariable" + }, + "parameters": [ + "CoinPitch", + "+", + "RandomWithStep(0.050,0.1,0.025)" + ] + } ] - } - ] - }, - { - "type": "BuiltinCommonInstructions::Standard", - "conditions": [], - "actions": [ + }, { - "type": { - "value": "Rotate" - }, - "parameters": [ - "Coin", - "35", - "" + "type": "BuiltinCommonInstructions::Standard", + "conditions": [], + "actions": [ + { + "type": { + "value": "Rotate" + }, + "parameters": [ + "Coin", + "35", + "" + ] + } ] } - ] + ], + "parameters": [] }, { "colorB": 228, @@ -2340,6 +2553,14 @@ "name": "Animation", "type": "AnimatableCapability::AnimatableBehavior" }, + { + "name": "ButtonFSM", + "type": "ButtonStates::ButtonFSM" + }, + { + "name": "ButtonScaleTween", + "type": "ButtonStates::ButtonScaleTween" + }, { "name": "Effect", "type": "EffectCapability::EffectBehavior" @@ -2391,6 +2612,10 @@ { "name": "ThirdPersonCamera", "type": "ThirdPersonCamera::ThirdPersonCamera" + }, + { + "name": "Tween", + "type": "Tween::TweenBehavior" } ] } @@ -20008,6 +20233,7296 @@ ] } ] + }, + { + "author": "", + "category": "User interface", + "dimension": "2D", + "extensionNamespace": "", + "fullName": "Button states and effects", + "gdevelopVersion": ">=5.5.222", + "helpPath": "/objects/button", + "iconUrl": "data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iVVRGLTgiPz48IURPQ1RZUEUgc3ZnIFBVQkxJQyAiLS8vVzNDLy9EVEQgU1ZHIDEuMS8vRU4iICJodHRwOi8vd3d3LnczLm9yZy9HcmFwaGljcy9TVkcvMS4xL0RURC9zdmcxMS5kdGQiPjxzdmcgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIiB4bWxuczp4bGluaz0iaHR0cDovL3d3dy53My5vcmcvMTk5OS94bGluayIgdmVyc2lvbj0iMS4xIiBpZD0ibWRpLWdlc3R1cmUtdGFwLWJ1dHRvbiIgd2lkdGg9IjI0IiBoZWlnaHQ9IjI0IiB2aWV3Qm94PSIwIDAgMjQgMjQiPjxwYXRoIGQ9Ik0xMyA1QzE1LjIxIDUgMTcgNi43OSAxNyA5QzE3IDEwLjUgMTYuMiAxMS43NyAxNSAxMi40NlYxMS4yNEMxNS42MSAxMC42OSAxNiA5Ljg5IDE2IDlDMTYgNy4zNCAxNC42NiA2IDEzIDZTMTAgNy4zNCAxMCA5QzEwIDkuODkgMTAuMzkgMTAuNjkgMTEgMTEuMjRWMTIuNDZDOS44IDExLjc3IDkgMTAuNSA5IDlDOSA2Ljc5IDEwLjc5IDUgMTMgNU0yMCAyMC41QzE5Ljk3IDIxLjMyIDE5LjMyIDIxLjk3IDE4LjUgMjJIMTNDMTIuNjIgMjIgMTIuMjYgMjEuODUgMTIgMjEuNTdMOCAxNy4zN0w4Ljc0IDE2LjZDOC45MyAxNi4zOSA5LjIgMTYuMjggOS41IDE2LjI4SDkuN0wxMiAxOFY5QzEyIDguNDUgMTIuNDUgOCAxMyA4UzE0IDguNDUgMTQgOVYxMy40N0wxNS4yMSAxMy42TDE5LjE1IDE1Ljc5QzE5LjY4IDE2LjAzIDIwIDE2LjU2IDIwIDE3LjE0VjIwLjVNMjAgMkg0QzIuOSAyIDIgMi45IDIgNFYxMkMyIDEzLjExIDIuOSAxNCA0IDE0SDhWMTJMNCAxMkw0IDRIMjBMMjAgMTJIMThWMTRIMjBWMTMuOTZMMjAuMDQgMTRDMjEuMTMgMTQgMjIgMTMuMDkgMjIgMTJWNEMyMiAyLjkgMjEuMTEgMiAyMCAyWiIgLz48L3N2Zz4=", + "name": "ButtonStates", + "previewIconUrl": "https://asset-resources.gdevelop.io/public-resources/Icons/753a9a794bd885058159b7509f06f5a8f67f72decfccb9a1b0efee26f41c3c4c_gesture-tap-button.svg", + "shortDescription": "Use any object as a button and change appearance according to user interactions.", + "version": "1.4.1", + "description": [ + "Use the \"Button states\" behavior to track user interactions with an object, including:", + "", + "- Hovered", + "- Pressed", + "- Clicked", + "- Idle", + "", + "Add additional behaviors to make juicy buttons with animated responses to user input:", + "", + "- Size", + "- Color", + "- Animation", + "- Object effects" + ], + "origin": { + "identifier": "ButtonStates", + "name": "gdevelop-extension-store" + }, + "tags": [ + "ui", + "button" + ], + "authorIds": [ + "IWykYNRvhCZBN3vEgKEbBPOR3Oc2", + "gqDaZjCfevOOxBYkK6zlhtZnXCg1" + ], + "dependencies": [], + "globalVariables": [], + "sceneVariables": [], + "eventsFunctions": [], + "eventsFunctionsFolderStructure": { + "folderName": "__ROOT" + }, + "eventsBasedBehaviors": [ + { + "description": "Use objects as buttons.", + "fullName": "Button states", + "helpPath": "", + "iconUrl": "", + "name": "ButtonFSM", + "objectType": "", + "previewIconUrl": "", + "eventsFunctions": [ + { + "fullName": "", + "functionType": "Action", + "name": "doStepPreEvents", + "sentence": "", + "events": [ + { + "colorB": 228, + "colorG": 176, + "colorR": 74, + "creationTime": 0, + "name": "Finite state machine", + "source": "", + "type": "BuiltinCommonInstructions::Group", + "events": [ + { + "type": "BuiltinCommonInstructions::Comment", + "color": { + "b": 109, + "g": 230, + "r": 255, + "textB": 0, + "textG": 0, + "textR": 0 + }, + "comment": "The \"Validated\" state only last one frame." + }, + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [ + { + "type": { + "value": "StringVariable" + }, + "parameters": [ + "State", + "=", + "\"Validated\"" + ] + } + ], + "actions": [ + { + "type": { + "value": "SetStringVariable" + }, + "parameters": [ + "State", + "=", + "\"Idle\"" + ] + } + ] + }, + { + "colorB": 228, + "colorG": 176, + "colorR": 74, + "creationTime": 0, + "name": "Check position", + "source": "", + "type": "BuiltinCommonInstructions::Group", + "events": [ + { + "type": "BuiltinCommonInstructions::Comment", + "color": { + "b": 109, + "g": 230, + "r": 255, + "textB": 0, + "textG": 0, + "textR": 0 + }, + "comment": "Make sure the cursor position is only checked once per frame." + }, + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [], + "actions": [ + { + "type": { + "value": "SetBooleanVariable" + }, + "parameters": [ + "MouseIsInside", + "False", + "" + ] + } + ] + }, + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [ + { + "type": { + "value": "BooleanVariable" + }, + "parameters": [ + "ShouldCheckHovering", + "True", + "" + ] + }, + { + "type": { + "value": "CollisionPoint" + }, + "parameters": [ + "Object", + "MouseOnlyCursorX(Object.Layer(), 0)", + "MouseOnlyCursorY(Object.Layer(), 0)" + ] + } + ], + "actions": [ + { + "type": { + "value": "SetBooleanVariable" + }, + "parameters": [ + "MouseIsInside", + "True", + "" + ] + } + ] + }, + { + "type": "BuiltinCommonInstructions::Comment", + "color": { + "b": 109, + "g": 230, + "r": 255, + "textB": 0, + "textG": 0, + "textR": 0 + }, + "comment": "Touches are always pressed, so ShouldCheckHovering doesn't matter." + }, + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [], + "actions": [ + { + "type": { + "value": "SetBooleanVariable" + }, + "parameters": [ + "TouchIsInside", + "False", + "" + ] + } + ] + }, + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [ + { + "type": { + "value": "NumberVariable" + }, + "parameters": [ + "TouchId", + "!=", + "0" + ] + }, + { + "type": { + "value": "CollisionPoint" + }, + "parameters": [ + "Object", + "TouchX(TouchId, Object.Layer(), 0)", + "TouchY(TouchId, Object.Layer(), 0)" + ] + } + ], + "actions": [ + { + "type": { + "value": "SetBooleanVariable" + }, + "parameters": [ + "TouchIsInside", + "True", + "" + ] + } + ] + } + ], + "parameters": [] + }, + { + "colorB": 228, + "colorG": 176, + "colorR": 74, + "creationTime": 0, + "name": "Handle touch start", + "source": "", + "type": "BuiltinCommonInstructions::Group", + "events": [ + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [ + { + "type": { + "value": "HasAnyTouchOrMouseStarted" + }, + "parameters": [ + "" + ] + } + ], + "actions": [ + { + "type": { + "value": "SetNumberVariable" + }, + "parameters": [ + "Index", + "=", + "0" + ] + } + ], + "events": [ + { + "type": "BuiltinCommonInstructions::Repeat", + "repeatExpression": "StartedTouchOrMouseCount()", + "conditions": [], + "actions": [], + "events": [ + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [ + { + "type": { + "value": "CollisionPoint" + }, + "parameters": [ + "Object", + "TouchX(StartedTouchOrMouseId(Index), Object.Layer(), 0)", + "TouchY(StartedTouchOrMouseId(Index), Object.Layer(), 0)" + ] + } + ], + "actions": [ + { + "type": { + "value": "SetNumberVariable" + }, + "parameters": [ + "TouchId", + "=", + "StartedTouchOrMouseId(Index)" + ] + }, + { + "type": { + "value": "SetBooleanVariable" + }, + "parameters": [ + "TouchIsInside", + "True", + "" + ] + } + ], + "events": [ + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [ + { + "type": { + "value": "BuiltinCommonInstructions::Or" + }, + "parameters": [], + "subInstructions": [ + { + "type": { + "value": "StringVariable" + }, + "parameters": [ + "State", + "=", + "\"Hovered\"" + ] + }, + { + "type": { + "value": "StringVariable" + }, + "parameters": [ + "State", + "=", + "\"Idle\"" + ] + } + ] + } + ], + "actions": [ + { + "type": { + "value": "SetStringVariable" + }, + "parameters": [ + "State", + "=", + "\"PressedInside\"" + ] + } + ] + } + ] + }, + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [], + "actions": [ + { + "type": { + "value": "SetNumberVariable" + }, + "parameters": [ + "Index", + "+", + "1" + ] + } + ] + } + ] + } + ] + } + ], + "parameters": [] + }, + { + "colorB": 228, + "colorG": 176, + "colorR": 74, + "creationTime": 0, + "name": "Apply position changes", + "source": "", + "type": "BuiltinCommonInstructions::Group", + "events": [ + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [ + { + "type": { + "inverted": true, + "value": "BooleanVariable" + }, + "parameters": [ + "MouseIsInside", + "True", + "" + ] + }, + { + "type": { + "value": "StringVariable" + }, + "parameters": [ + "State", + "=", + "\"Hovered\"" + ] + } + ], + "actions": [ + { + "type": { + "value": "SetStringVariable" + }, + "parameters": [ + "State", + "=", + "\"Idle\"" + ] + } + ] + }, + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [ + { + "type": { + "value": "BooleanVariable" + }, + "parameters": [ + "MouseIsInside", + "True", + "" + ] + }, + { + "type": { + "value": "StringVariable" + }, + "parameters": [ + "State", + "=", + "\"Idle\"" + ] + } + ], + "actions": [ + { + "type": { + "value": "SetStringVariable" + }, + "parameters": [ + "State", + "=", + "\"Hovered\"" + ] + } + ] + }, + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [ + { + "type": { + "inverted": true, + "value": "BooleanVariable" + }, + "parameters": [ + "TouchIsInside", + "True", + "" + ] + }, + { + "type": { + "value": "StringVariable" + }, + "parameters": [ + "State", + "=", + "\"PressedInside\"" + ] + } + ], + "actions": [ + { + "type": { + "value": "SetStringVariable" + }, + "parameters": [ + "State", + "=", + "\"PressedOutside\"" + ] + } + ] + }, + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [ + { + "type": { + "value": "BooleanVariable" + }, + "parameters": [ + "TouchIsInside", + "True", + "" + ] + }, + { + "type": { + "value": "StringVariable" + }, + "parameters": [ + "State", + "=", + "\"PressedOutside\"" + ] + } + ], + "actions": [ + { + "type": { + "value": "SetStringVariable" + }, + "parameters": [ + "State", + "=", + "\"PressedInside\"" + ] + } + ] + } + ], + "parameters": [] + }, + { + "colorB": 228, + "colorG": 176, + "colorR": 74, + "creationTime": 0, + "name": "Handle touch end", + "source": "", + "type": "BuiltinCommonInstructions::Group", + "events": [ + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [ + { + "type": { + "value": "HasTouchEnded" + }, + "parameters": [ + "", + "TouchId" + ] + } + ], + "actions": [ + { + "type": { + "value": "SetNumberVariable" + }, + "parameters": [ + "TouchId", + "=", + "0" + ] + } + ], + "events": [ + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [ + { + "type": { + "value": "StringVariable" + }, + "parameters": [ + "State", + "=", + "\"PressedInside\"" + ] + } + ], + "actions": [ + { + "type": { + "value": "SetStringVariable" + }, + "parameters": [ + "State", + "=", + "\"Validated\"" + ] + } + ] + }, + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [ + { + "type": { + "inverted": true, + "value": "StringVariable" + }, + "parameters": [ + "State", + "=", + "\"PressedInside\"" + ] + }, + { + "type": { + "inverted": true, + "value": "StringVariable" + }, + "parameters": [ + "State", + "=", + "\"Validated\"" + ] + } + ], + "actions": [ + { + "type": { + "value": "SetStringVariable" + }, + "parameters": [ + "State", + "=", + "\"Idle\"" + ] + } + ] + } + ] + } + ], + "parameters": [] + } + ], + "parameters": [] + } + ], + "parameters": [ + { + "description": "Object", + "name": "Object", + "type": "object" + }, + { + "description": "Behavior", + "name": "Behavior", + "supplementaryInformation": "ButtonStates::ButtonFSM", + "type": "behavior" + } + ], + "objectGroups": [] + }, + { + "fullName": "", + "functionType": "Action", + "name": "onDeActivate", + "sentence": "", + "events": [ + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [], + "actions": [ + { + "type": { + "value": "ButtonStates::ButtonFSM::ResetState" + }, + "parameters": [ + "Object", + "Behavior", + "" + ] + } + ] + } + ], + "parameters": [ + { + "description": "Object", + "name": "Object", + "type": "object" + }, + { + "description": "Behavior", + "name": "Behavior", + "supplementaryInformation": "ButtonStates::ButtonFSM", + "type": "behavior" + } + ], + "objectGroups": [] + }, + { + "description": "Reset the state of the button.", + "fullName": "Reset state", + "functionType": "Action", + "name": "ResetState", + "private": true, + "sentence": "Reset the button state of _PARAM0_", + "events": [ + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [], + "actions": [ + { + "type": { + "value": "SetStringVariable" + }, + "parameters": [ + "State", + "=", + "\"Idle\"" + ] + }, + { + "type": { + "value": "SetNumberVariable" + }, + "parameters": [ + "TouchId", + "=", + "0" + ] + } + ] + } + ], + "parameters": [ + { + "description": "Object", + "name": "Object", + "type": "object" + }, + { + "description": "Behavior", + "name": "Behavior", + "supplementaryInformation": "ButtonStates::ButtonFSM", + "type": "behavior" + } + ], + "objectGroups": [] + }, + { + "description": "Check if the button is not used.", + "fullName": "Is idle", + "functionType": "Condition", + "name": "IsIdle", + "sentence": "_PARAM0_ is idle", + "events": [ + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [ + { + "type": { + "value": "StringVariable" + }, + "parameters": [ + "State", + "=", + "\"Idle\"" + ] + } + ], + "actions": [ + { + "type": { + "value": "SetReturnBoolean" + }, + "parameters": [ + "True" + ] + } + ] + } + ], + "parameters": [ + { + "description": "Object", + "name": "Object", + "type": "object" + }, + { + "description": "Behavior", + "name": "Behavior", + "supplementaryInformation": "ButtonStates::ButtonFSM", + "type": "behavior" + } + ], + "objectGroups": [] + }, + { + "description": "Check if the button was just clicked.", + "fullName": "Is clicked", + "functionType": "Condition", + "name": "IsClicked", + "sentence": "_PARAM0_ is clicked", + "events": [ + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [ + { + "type": { + "value": "StringVariable" + }, + "parameters": [ + "State", + "=", + "\"Validated\"" + ] + } + ], + "actions": [ + { + "type": { + "value": "SetReturnBoolean" + }, + "parameters": [ + "True" + ] + } + ] + } + ], + "parameters": [ + { + "description": "Object", + "name": "Object", + "type": "object" + }, + { + "description": "Behavior", + "name": "Behavior", + "supplementaryInformation": "ButtonStates::ButtonFSM", + "type": "behavior" + } + ], + "objectGroups": [] + }, + { + "description": "Check if the cursor is hovered over the button.", + "fullName": "Is hovered", + "functionType": "Condition", + "name": "IsHovered", + "sentence": "_PARAM0_ is hovered", + "events": [ + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [ + { + "type": { + "value": "StringVariable" + }, + "parameters": [ + "State", + "=", + "\"Hovered\"" + ] + } + ], + "actions": [ + { + "type": { + "value": "SetReturnBoolean" + }, + "parameters": [ + "True" + ] + } + ] + } + ], + "parameters": [ + { + "description": "Object", + "name": "Object", + "type": "object" + }, + { + "description": "Behavior", + "name": "Behavior", + "supplementaryInformation": "ButtonStates::ButtonFSM", + "type": "behavior" + } + ], + "objectGroups": [] + }, + { + "description": "Check if the button is either hovered or pressed but not hovered.", + "fullName": "Is focused", + "functionType": "Condition", + "name": "IsFocused", + "sentence": "_PARAM0_ is focused", + "events": [ + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [ + { + "type": { + "value": "StringVariable" + }, + "parameters": [ + "State", + "=", + "\"Hovered\"" + ] + } + ], + "actions": [ + { + "type": { + "value": "SetReturnBoolean" + }, + "parameters": [ + "True" + ] + } + ] + }, + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [ + { + "type": { + "value": "StringVariable" + }, + "parameters": [ + "State", + "=", + "\"PressedOutside\"" + ] + } + ], + "actions": [ + { + "type": { + "value": "SetReturnBoolean" + }, + "parameters": [ + "True" + ] + } + ] + } + ], + "parameters": [ + { + "description": "Object", + "name": "Object", + "type": "object" + }, + { + "description": "Behavior", + "name": "Behavior", + "supplementaryInformation": "ButtonStates::ButtonFSM", + "type": "behavior" + } + ], + "objectGroups": [] + }, + { + "description": "Check if the button is currently being pressed with mouse or touch.", + "fullName": "Is pressed", + "functionType": "Condition", + "name": "IsPressed", + "sentence": "_PARAM0_ is pressed", + "events": [ + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [ + { + "type": { + "value": "StringVariable" + }, + "parameters": [ + "State", + "=", + "\"PressedInside\"" + ] + } + ], + "actions": [ + { + "type": { + "value": "SetReturnBoolean" + }, + "parameters": [ + "True" + ] + } + ] + } + ], + "parameters": [ + { + "description": "Object", + "name": "Object", + "type": "object" + }, + { + "description": "Behavior", + "name": "Behavior", + "supplementaryInformation": "ButtonStates::ButtonFSM", + "type": "behavior" + } + ], + "objectGroups": [] + }, + { + "description": "Check if the button is currently being pressed outside with mouse or touch.", + "fullName": "Is held outside", + "functionType": "Condition", + "name": "IsPressedOutside", + "sentence": "_PARAM0_ is held outside", + "events": [ + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [ + { + "type": { + "value": "StringVariable" + }, + "parameters": [ + "State", + "=", + "\"PressedOutside\"" + ] + } + ], + "actions": [ + { + "type": { + "value": "SetReturnBoolean" + }, + "parameters": [ + "True" + ] + } + ] + } + ], + "parameters": [ + { + "description": "Object", + "name": "Object", + "type": "object" + }, + { + "description": "Behavior", + "name": "Behavior", + "supplementaryInformation": "ButtonStates::ButtonFSM", + "type": "behavior" + } + ], + "objectGroups": [] + }, + { + "description": "the touch id that is using the button or 0 if none.", + "fullName": "Touch id", + "functionType": "ExpressionAndCondition", + "name": "TouchId", + "sentence": "the touch id", + "events": [ + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [], + "actions": [ + { + "type": { + "value": "SetReturnNumber" + }, + "parameters": [ + "TouchId" + ] + } + ] + } + ], + "expressionType": { + "type": "expression" + }, + "parameters": [ + { + "description": "Object", + "name": "Object", + "type": "object" + }, + { + "description": "Behavior", + "name": "Behavior", + "supplementaryInformation": "ButtonStates::ButtonFSM", + "type": "behavior" + } + ], + "objectGroups": [] + } + ], + "eventsFunctionsFolderStructure": { + "folderName": "__ROOT", + "children": [ + { + "functionName": "doStepPreEvents" + }, + { + "functionName": "onDeActivate" + }, + { + "functionName": "ResetState" + }, + { + "functionName": "IsIdle" + }, + { + "functionName": "IsClicked" + }, + { + "functionName": "IsHovered" + }, + { + "functionName": "IsFocused" + }, + { + "functionName": "IsPressed" + }, + { + "functionName": "IsPressedOutside" + }, + { + "functionName": "TouchId" + } + ] + }, + "propertyDescriptors": [ + { + "value": "true", + "type": "Boolean", + "label": "", + "description": "Should check hovering", + "hidden": true, + "name": "ShouldCheckHovering" + }, + { + "value": "Idle", + "type": "Choice", + "label": "State", + "choices": [ + { + "label": "Idle", + "value": "Idle" + }, + { + "label": "Hovered", + "value": "Hovered" + }, + { + "label": "PressedInside", + "value": "PressedInside" + }, + { + "label": "PressedOutside", + "value": "PressedOutside" + }, + { + "label": "Validated", + "value": "Validated" + } + ], + "hidden": true, + "name": "State" + }, + { + "value": "0", + "type": "Number", + "label": "Touch id", + "hidden": true, + "name": "TouchId" + }, + { + "value": "", + "type": "Boolean", + "label": "Touch is inside", + "hidden": true, + "name": "TouchIsInside" + }, + { + "value": "", + "type": "Boolean", + "label": "Mouse is inside", + "hidden": true, + "name": "MouseIsInside" + }, + { + "value": "", + "type": "Number", + "label": "", + "hidden": true, + "name": "Index" + } + ], + "propertiesFolderStructure": { + "folderName": "__ROOT", + "children": [ + { + "propertyName": "ShouldCheckHovering" + }, + { + "propertyName": "State" + }, + { + "propertyName": "TouchId" + }, + { + "propertyName": "TouchIsInside" + }, + { + "propertyName": "MouseIsInside" + }, + { + "propertyName": "Index" + } + ] + } + }, + { + "description": "Enable effects on buttons based on their state.", + "fullName": "Button object effects", + "helpPath": "", + "iconUrl": "", + "name": "ButtonObjectEffects", + "objectType": "", + "previewIconUrl": "", + "eventsFunctions": [ + { + "fullName": "", + "functionType": "Action", + "name": "onCreated", + "sentence": "", + "events": [ + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [], + "actions": [ + { + "type": { + "value": "EffectCapability::EffectBehavior::EnableEffect" + }, + "parameters": [ + "Object", + "Effect", + "IdleEffect", + "yes" + ] + }, + { + "type": { + "value": "EffectCapability::EffectBehavior::EnableEffect" + }, + "parameters": [ + "Object", + "Effect", + "FocusedEffect", + "no" + ] + }, + { + "type": { + "value": "EffectCapability::EffectBehavior::EnableEffect" + }, + "parameters": [ + "Object", + "Effect", + "PressedEffect", + "no" + ] + } + ] + } + ], + "parameters": [ + { + "description": "Object", + "name": "Object", + "type": "object" + }, + { + "description": "Behavior", + "name": "Behavior", + "supplementaryInformation": "ButtonStates::ButtonObjectEffects", + "type": "behavior" + } + ], + "objectGroups": [] + }, + { + "fullName": "", + "functionType": "Action", + "name": "doStepPreEvents", + "sentence": "", + "events": [ + { + "colorB": 228, + "colorG": 176, + "colorR": 74, + "creationTime": 0, + "name": "States", + "source": "", + "type": "BuiltinCommonInstructions::Group", + "events": [ + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [], + "actions": [ + { + "type": { + "value": "EffectCapability::EffectBehavior::EnableEffect" + }, + "parameters": [ + "Object", + "Effect", + "IdleEffect", + "no" + ] + }, + { + "type": { + "value": "EffectCapability::EffectBehavior::EnableEffect" + }, + "parameters": [ + "Object", + "Effect", + "FocusedEffect", + "no" + ] + }, + { + "type": { + "value": "EffectCapability::EffectBehavior::EnableEffect" + }, + "parameters": [ + "Object", + "Effect", + "PressedEffect", + "no" + ] + } + ] + }, + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [ + { + "type": { + "value": "StringVariable" + }, + "parameters": [ + "IdleEffect", + "!=", + "\"\"" + ] + }, + { + "type": { + "value": "ButtonStates::ButtonFSM::IsIdle" + }, + "parameters": [ + "Object", + "ButtonFSM", + "" + ] + } + ], + "actions": [ + { + "type": { + "value": "EffectCapability::EffectBehavior::EnableEffect" + }, + "parameters": [ + "Object", + "Effect", + "IdleEffect", + "yes" + ] + } + ] + }, + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [ + { + "type": { + "value": "StringVariable" + }, + "parameters": [ + "FocusedEffect", + "!=", + "\"\"" + ] + }, + { + "type": { + "value": "ButtonStates::ButtonFSM::IsFocused" + }, + "parameters": [ + "Object", + "ButtonFSM", + "" + ] + } + ], + "actions": [ + { + "type": { + "value": "EffectCapability::EffectBehavior::EnableEffect" + }, + "parameters": [ + "Object", + "Effect", + "FocusedEffect", + "yes" + ] + } + ] + }, + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [ + { + "type": { + "value": "StringVariable" + }, + "parameters": [ + "PressedEffect", + "!=", + "\"\"" + ] + }, + { + "type": { + "value": "ButtonStates::ButtonFSM::IsPressed" + }, + "parameters": [ + "Object", + "ButtonFSM", + "" + ] + } + ], + "actions": [ + { + "type": { + "value": "EffectCapability::EffectBehavior::EnableEffect" + }, + "parameters": [ + "Object", + "Effect", + "PressedEffect", + "yes" + ] + } + ] + } + ], + "parameters": [] + } + ], + "parameters": [ + { + "description": "Object", + "name": "Object", + "type": "object" + }, + { + "description": "Behavior", + "name": "Behavior", + "supplementaryInformation": "ButtonStates::ButtonObjectEffects", + "type": "behavior" + } + ], + "objectGroups": [] + }, + { + "description": "the idle state effect of the object.", + "fullName": "Idle state effect", + "functionType": "ExpressionAndCondition", + "group": "Button object effects configuration", + "name": "IdleEffect", + "sentence": "the idle state effect", + "events": [ + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [], + "actions": [ + { + "type": { + "value": "SetReturnString" + }, + "parameters": [ + "IdleEffect" + ] + } + ] + } + ], + "expressionType": { + "type": "string" + }, + "parameters": [ + { + "description": "Object", + "name": "Object", + "type": "object" + }, + { + "description": "Behavior", + "name": "Behavior", + "supplementaryInformation": "ButtonStates::ButtonObjectEffects", + "type": "behavior" + } + ], + "objectGroups": [] + }, + { + "fullName": "", + "functionType": "ActionWithOperator", + "getterName": "IdleEffect", + "group": "Button object effects configuration", + "name": "SetIdleEffect", + "sentence": "", + "events": [ + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [], + "actions": [ + { + "type": { + "value": "SetStringVariable" + }, + "parameters": [ + "IdleEffect", + "=", + "Value" + ] + } + ] + } + ], + "parameters": [ + { + "description": "Object", + "name": "Object", + "type": "object" + }, + { + "description": "Behavior", + "name": "Behavior", + "supplementaryInformation": "ButtonStates::ButtonObjectEffects", + "type": "behavior" + } + ], + "objectGroups": [] + }, + { + "description": "the focused state effect of the object. The state is Focused when the button is hovered or held outside.", + "fullName": "Focused state effect", + "functionType": "ExpressionAndCondition", + "group": "Button object effects configuration", + "name": "FocusedEffect", + "sentence": "the focused state effect", + "events": [ + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [], + "actions": [ + { + "type": { + "value": "SetReturnString" + }, + "parameters": [ + "FocusedEffect" + ] + } + ] + } + ], + "expressionType": { + "type": "string" + }, + "parameters": [ + { + "description": "Object", + "name": "Object", + "type": "object" + }, + { + "description": "Behavior", + "name": "Behavior", + "supplementaryInformation": "ButtonStates::ButtonObjectEffects", + "type": "behavior" + } + ], + "objectGroups": [] + }, + { + "fullName": "", + "functionType": "ActionWithOperator", + "getterName": "FocusedEffect", + "group": "Button object effects configuration", + "name": "SetFocusedEffect", + "sentence": "", + "events": [ + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [], + "actions": [ + { + "type": { + "value": "SetStringVariable" + }, + "parameters": [ + "FocusedEffect", + "=", + "Value" + ] + } + ] + } + ], + "parameters": [ + { + "description": "Object", + "name": "Object", + "type": "object" + }, + { + "description": "Behavior", + "name": "Behavior", + "supplementaryInformation": "ButtonStates::ButtonObjectEffects", + "type": "behavior" + } + ], + "objectGroups": [] + }, + { + "description": "the pressed state effect of the object.", + "fullName": "Pressed state effect", + "functionType": "ExpressionAndCondition", + "group": "Button object effects configuration", + "name": "PressedEffect", + "sentence": "the pressed state effect", + "events": [ + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [], + "actions": [ + { + "type": { + "value": "SetReturnString" + }, + "parameters": [ + "PressedEffect" + ] + } + ] + } + ], + "expressionType": { + "type": "string" + }, + "parameters": [ + { + "description": "Object", + "name": "Object", + "type": "object" + }, + { + "description": "Behavior", + "name": "Behavior", + "supplementaryInformation": "ButtonStates::ButtonObjectEffects", + "type": "behavior" + } + ], + "objectGroups": [] + }, + { + "fullName": "", + "functionType": "ActionWithOperator", + "getterName": "PressedEffect", + "group": "Button object effects configuration", + "name": "SetPressedEffect", + "sentence": "", + "events": [ + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [], + "actions": [ + { + "type": { + "value": "SetStringVariable" + }, + "parameters": [ + "PressedEffect", + "=", + "Value" + ] + } + ] + } + ], + "parameters": [ + { + "description": "Object", + "name": "Object", + "type": "object" + }, + { + "description": "Behavior", + "name": "Behavior", + "supplementaryInformation": "ButtonStates::ButtonObjectEffects", + "type": "behavior" + } + ], + "objectGroups": [] + } + ], + "eventsFunctionsFolderStructure": { + "folderName": "__ROOT", + "children": [ + { + "functionName": "onCreated" + }, + { + "functionName": "doStepPreEvents" + }, + { + "folderName": "Button object effects configuration", + "children": [ + { + "functionName": "IdleEffect" + }, + { + "functionName": "SetIdleEffect" + }, + { + "functionName": "FocusedEffect" + }, + { + "functionName": "SetFocusedEffect" + }, + { + "functionName": "PressedEffect" + }, + { + "functionName": "SetPressedEffect" + } + ] + } + ] + }, + "propertyDescriptors": [ + { + "value": "", + "type": "Behavior", + "label": "Button states", + "extraInformation": [ + "ButtonStates::ButtonFSM" + ], + "choices": [], + "name": "ButtonFSM" + }, + { + "value": "", + "type": "Behavior", + "label": "Effect capability", + "extraInformation": [ + "EffectCapability::EffectBehavior" + ], + "choices": [], + "name": "Effect" + }, + { + "value": "", + "type": "String", + "label": "Idle state effect", + "group": "Effects", + "name": "IdleEffect" + }, + { + "value": "", + "type": "String", + "label": "Focused state effect", + "description": "The state is Focused when the button is hovered or held outside.", + "group": "Effects", + "name": "FocusedEffect" + }, + { + "value": "", + "type": "String", + "label": "Pressed state effect", + "group": "Effects", + "name": "PressedEffect" + } + ], + "propertiesFolderStructure": { + "folderName": "__ROOT", + "children": [ + { + "propertyName": "ButtonFSM" + }, + { + "propertyName": "Effect" + }, + { + "folderName": "Effects", + "children": [ + { + "propertyName": "IdleEffect" + }, + { + "propertyName": "FocusedEffect" + }, + { + "propertyName": "PressedEffect" + } + ] + } + ] + } + }, + { + "description": "Change the animation of buttons according to their state.", + "fullName": "Button animation", + "helpPath": "", + "iconUrl": "", + "name": "ButtonAnimationName", + "objectType": "", + "previewIconUrl": "", + "eventsFunctions": [ + { + "fullName": "", + "functionType": "Action", + "name": "onCreated", + "sentence": "", + "events": [ + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [], + "actions": [ + { + "type": { + "value": "AnimatableCapability::AnimatableBehavior::SetName" + }, + "parameters": [ + "Object", + "Animation", + "=", + "IdleAnimationName" + ] + } + ] + } + ], + "parameters": [ + { + "description": "Object", + "name": "Object", + "type": "object" + }, + { + "description": "Behavior", + "name": "Behavior", + "supplementaryInformation": "ButtonStates::ButtonAnimationName", + "type": "behavior" + } + ], + "objectGroups": [] + }, + { + "fullName": "", + "functionType": "Action", + "name": "doStepPreEvents", + "sentence": "", + "events": [ + { + "colorB": 228, + "colorG": 176, + "colorR": 74, + "creationTime": 0, + "name": "Change based on state", + "source": "", + "type": "BuiltinCommonInstructions::Group", + "events": [ + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [ + { + "type": { + "value": "ButtonStates::ButtonFSM::IsIdle" + }, + "parameters": [ + "Object", + "ButtonFSM", + "" + ] + } + ], + "actions": [ + { + "type": { + "value": "AnimatableCapability::AnimatableBehavior::SetName" + }, + "parameters": [ + "Object", + "Animation", + "=", + "IdleAnimationName" + ] + } + ] + }, + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [ + { + "type": { + "value": "ButtonStates::ButtonFSM::IsFocused" + }, + "parameters": [ + "Object", + "ButtonFSM", + "" + ] + } + ], + "actions": [ + { + "type": { + "value": "AnimatableCapability::AnimatableBehavior::SetName" + }, + "parameters": [ + "Object", + "Animation", + "=", + "FocusedAnimationName" + ] + } + ] + }, + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [ + { + "type": { + "value": "ButtonStates::ButtonFSM::IsPressed" + }, + "parameters": [ + "Object", + "ButtonFSM", + "" + ] + } + ], + "actions": [ + { + "type": { + "value": "AnimatableCapability::AnimatableBehavior::SetName" + }, + "parameters": [ + "Object", + "Animation", + "=", + "PressedAnimationName" + ] + } + ] + } + ], + "parameters": [] + } + ], + "parameters": [ + { + "description": "Object", + "name": "Object", + "type": "object" + }, + { + "description": "Behavior", + "name": "Behavior", + "supplementaryInformation": "ButtonStates::ButtonAnimationName", + "type": "behavior" + } + ], + "objectGroups": [] + }, + { + "description": "the idle state animation name of the object.", + "fullName": "Idle state animation name", + "functionType": "ExpressionAndCondition", + "group": "Button animation configuration", + "name": "IdleAnimationName", + "sentence": "the idle state animation name", + "events": [ + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [], + "actions": [ + { + "type": { + "value": "SetReturnString" + }, + "parameters": [ + "IdleAnimationName" + ] + } + ] + } + ], + "expressionType": { + "type": "string" + }, + "parameters": [ + { + "description": "Object", + "name": "Object", + "type": "object" + }, + { + "description": "Behavior", + "name": "Behavior", + "supplementaryInformation": "ButtonStates::ButtonAnimationName", + "type": "behavior" + } + ], + "objectGroups": [] + }, + { + "fullName": "", + "functionType": "ActionWithOperator", + "getterName": "IdleAnimationName", + "group": "Button animation configuration", + "name": "SetIdleAnimationName", + "sentence": "", + "events": [ + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [], + "actions": [ + { + "type": { + "value": "SetStringVariable" + }, + "parameters": [ + "IdleAnimationName", + "=", + "Value" + ] + } + ] + } + ], + "parameters": [ + { + "description": "Object", + "name": "Object", + "type": "object" + }, + { + "description": "Behavior", + "name": "Behavior", + "supplementaryInformation": "ButtonStates::ButtonAnimationName", + "type": "behavior" + } + ], + "objectGroups": [] + }, + { + "description": "the focused state animation name of the object. The state is Focused when the button is hovered or held outside.", + "fullName": "Focused state animation name", + "functionType": "ExpressionAndCondition", + "group": "Button animation configuration", + "name": "FocusedAnimationName", + "sentence": "the focused state animation name", + "events": [ + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [], + "actions": [ + { + "type": { + "value": "SetReturnString" + }, + "parameters": [ + "FocusedAnimationName" + ] + } + ] + } + ], + "expressionType": { + "type": "string" + }, + "parameters": [ + { + "description": "Object", + "name": "Object", + "type": "object" + }, + { + "description": "Behavior", + "name": "Behavior", + "supplementaryInformation": "ButtonStates::ButtonAnimationName", + "type": "behavior" + } + ], + "objectGroups": [] + }, + { + "fullName": "", + "functionType": "ActionWithOperator", + "getterName": "FocusedAnimationName", + "group": "Button animation configuration", + "name": "SetFocusedAnimationName", + "sentence": "", + "events": [ + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [], + "actions": [ + { + "type": { + "value": "SetStringVariable" + }, + "parameters": [ + "FocusedAnimationName", + "=", + "Value" + ] + } + ] + } + ], + "parameters": [ + { + "description": "Object", + "name": "Object", + "type": "object" + }, + { + "description": "Behavior", + "name": "Behavior", + "supplementaryInformation": "ButtonStates::ButtonAnimationName", + "type": "behavior" + } + ], + "objectGroups": [] + }, + { + "description": "the pressed state animation name of the object.", + "fullName": "Pressed state animation name", + "functionType": "ExpressionAndCondition", + "group": "Button animation configuration", + "name": "PressedAnimationName", + "sentence": "the pressed state animation name", + "events": [ + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [], + "actions": [ + { + "type": { + "value": "SetReturnString" + }, + "parameters": [ + "PressedAnimationName" + ] + } + ] + } + ], + "expressionType": { + "type": "string" + }, + "parameters": [ + { + "description": "Object", + "name": "Object", + "type": "object" + }, + { + "description": "Behavior", + "name": "Behavior", + "supplementaryInformation": "ButtonStates::ButtonAnimationName", + "type": "behavior" + } + ], + "objectGroups": [] + }, + { + "fullName": "", + "functionType": "ActionWithOperator", + "getterName": "PressedAnimationName", + "group": "Button animation configuration", + "name": "SetPressedAnimationName", + "sentence": "", + "events": [ + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [], + "actions": [ + { + "type": { + "value": "SetStringVariable" + }, + "parameters": [ + "PressedAnimationName", + "=", + "Value" + ] + } + ] + } + ], + "parameters": [ + { + "description": "Object", + "name": "Object", + "type": "object" + }, + { + "description": "Behavior", + "name": "Behavior", + "supplementaryInformation": "ButtonStates::ButtonAnimationName", + "type": "behavior" + } + ], + "objectGroups": [] + } + ], + "eventsFunctionsFolderStructure": { + "folderName": "__ROOT", + "children": [ + { + "functionName": "onCreated" + }, + { + "functionName": "doStepPreEvents" + }, + { + "folderName": "Button animation configuration", + "children": [ + { + "functionName": "IdleAnimationName" + }, + { + "functionName": "SetIdleAnimationName" + }, + { + "functionName": "FocusedAnimationName" + }, + { + "functionName": "SetFocusedAnimationName" + }, + { + "functionName": "PressedAnimationName" + }, + { + "functionName": "SetPressedAnimationName" + } + ] + } + ] + }, + "propertyDescriptors": [ + { + "value": "", + "type": "Behavior", + "label": "Animatable capability", + "extraInformation": [ + "AnimatableCapability::AnimatableBehavior" + ], + "choices": [], + "name": "Animation" + }, + { + "value": "", + "type": "Behavior", + "label": "Button states", + "extraInformation": [ + "ButtonStates::ButtonFSM" + ], + "choices": [], + "name": "ButtonFSM" + }, + { + "value": "Idle", + "type": "String", + "label": "Idle state animation name", + "group": "Animation", + "name": "IdleAnimationName" + }, + { + "value": "Focused", + "type": "String", + "label": "Focused state animation name", + "description": "The state is Focused when the button is hovered or held outside.", + "group": "Animation", + "name": "FocusedAnimationName" + }, + { + "value": "Pressed", + "type": "String", + "label": "Pressed state animation name", + "group": "Animation", + "name": "PressedAnimationName" + } + ], + "propertiesFolderStructure": { + "folderName": "__ROOT", + "children": [ + { + "propertyName": "Animation" + }, + { + "propertyName": "ButtonFSM" + }, + { + "folderName": "Animation", + "children": [ + { + "propertyName": "IdleAnimationName" + }, + { + "propertyName": "FocusedAnimationName" + }, + { + "propertyName": "PressedAnimationName" + } + ] + } + ] + } + }, + { + "description": "Smoothly change an effect on buttons according to their state.", + "fullName": "Button object effect tween", + "helpPath": "", + "iconUrl": "", + "name": "ButtonObjectEffectTween", + "objectType": "", + "previewIconUrl": "", + "eventsFunctions": [ + { + "fullName": "", + "functionType": "Action", + "name": "onCreated", + "sentence": "", + "events": [ + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [], + "actions": [ + { + "type": { + "value": "SetNumberVariable" + }, + "parameters": [ + "EffectValue", + "=", + "IdleValue" + ] + } + ] + } + ], + "parameters": [ + { + "description": "Object", + "name": "Object", + "type": "object" + }, + { + "description": "Behavior", + "name": "Behavior", + "supplementaryInformation": "ButtonStates::ButtonObjectEffectTween", + "type": "behavior" + } + ], + "objectGroups": [] + }, + { + "fullName": "", + "functionType": "Action", + "name": "doStepPreEvents", + "sentence": "", + "events": [ + { + "colorB": 228, + "colorG": 176, + "colorR": 74, + "creationTime": 0, + "name": "States", + "source": "", + "type": "BuiltinCommonInstructions::Group", + "events": [ + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [ + { + "type": { + "value": "ButtonStates::ButtonFSM::IsIdle" + }, + "parameters": [ + "Object", + "ButtonFSM", + "" + ] + }, + { + "type": { + "value": "StringVariable" + }, + "parameters": [ + "PreviousState", + "!=", + "\"Idle\"" + ] + } + ], + "actions": [ + { + "type": { + "value": "SetStringVariable" + }, + "parameters": [ + "PreviousState", + "=", + "\"Idle\"" + ] + }, + { + "type": { + "value": "ButtonStates::ButtonObjectEffectTween::FadeOut" + }, + "parameters": [ + "Object", + "Behavior", + "IdleValue", + "" + ] + } + ] + }, + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [ + { + "type": { + "value": "ButtonStates::ButtonFSM::IsFocused" + }, + "parameters": [ + "Object", + "ButtonFSM", + "" + ] + } + ], + "actions": [], + "events": [ + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [ + { + "type": { + "value": "StringVariable" + }, + "parameters": [ + "PreviousState", + "=", + "\"Idle\"" + ] + } + ], + "actions": [ + { + "type": { + "value": "SetStringVariable" + }, + "parameters": [ + "PreviousState", + "=", + "\"Focused\"" + ] + }, + { + "type": { + "value": "ButtonStates::ButtonObjectEffectTween::FadeIn" + }, + "parameters": [ + "Object", + "Behavior", + "FocusedValue", + "" + ] + } + ] + }, + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [ + { + "type": { + "value": "StringVariable" + }, + "parameters": [ + "PreviousState", + "=", + "\"Pressed\"" + ] + } + ], + "actions": [ + { + "type": { + "value": "SetStringVariable" + }, + "parameters": [ + "PreviousState", + "=", + "\"Focused\"" + ] + }, + { + "type": { + "value": "ButtonStates::ButtonObjectEffectTween::FadeOut" + }, + "parameters": [ + "Object", + "Behavior", + "FocusedValue", + "" + ] + } + ] + } + ] + }, + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [ + { + "type": { + "value": "ButtonStates::ButtonFSM::IsPressed" + }, + "parameters": [ + "Object", + "ButtonFSM", + "" + ] + }, + { + "type": { + "value": "StringVariable" + }, + "parameters": [ + "PreviousState", + "!=", + "\"Pressed\"" + ] + } + ], + "actions": [ + { + "type": { + "value": "SetStringVariable" + }, + "parameters": [ + "PreviousState", + "=", + "\"Pressed\"" + ] + }, + { + "type": { + "value": "ButtonStates::ButtonObjectEffectTween::FadeIn" + }, + "parameters": [ + "Object", + "Behavior", + "PressedValue", + "" + ] + } + ] + } + ], + "parameters": [] + }, + { + "colorB": 228, + "colorG": 176, + "colorR": 74, + "creationTime": 0, + "name": "Tween", + "source": "", + "type": "BuiltinCommonInstructions::Group", + "events": [ + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [ + { + "type": { + "value": "StringVariable" + }, + "parameters": [ + "TweenState", + "=", + "\"FadeIn\"" + ] + } + ], + "actions": [ + { + "type": { + "value": "ButtonStates::ButtonObjectEffectTween::PlayTween" + }, + "parameters": [ + "Object", + "Behavior", + "FadeInDuration", + "FadeInEasing", + "" + ] + } + ] + }, + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [ + { + "type": { + "value": "StringVariable" + }, + "parameters": [ + "TweenState", + "=", + "\"FadeOut\"" + ] + } + ], + "actions": [ + { + "type": { + "value": "ButtonStates::ButtonObjectEffectTween::PlayTween" + }, + "parameters": [ + "Object", + "Behavior", + "FadeOutDuration", + "FadeOutEasing", + "" + ] + } + ] + } + ], + "parameters": [] + }, + { + "colorB": 228, + "colorG": 176, + "colorR": 74, + "creationTime": 0, + "name": "Toggle effect", + "source": "", + "type": "BuiltinCommonInstructions::Group", + "events": [ + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [ + { + "type": { + "value": "BooleanVariable" + }, + "parameters": [ + "ShouldDisableEffectInIdle", + "True", + "" + ] + } + ], + "actions": [], + "events": [ + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [ + { + "type": { + "value": "ButtonStates::ButtonFSM::IsIdle" + }, + "parameters": [ + "Object", + "ButtonFSM", + "" + ] + }, + { + "type": { + "value": "StringVariable" + }, + "parameters": [ + "TweenState", + "=", + "\"NoTween\"" + ] + } + ], + "actions": [ + { + "type": { + "value": "EffectCapability::EffectBehavior::EnableEffect" + }, + "parameters": [ + "Object", + "Effect", + "EffectName", + "no" + ] + } + ] + }, + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [ + { + "type": { + "inverted": true, + "value": "ButtonStates::ButtonFSM::IsIdle" + }, + "parameters": [ + "Object", + "ButtonFSM", + "" + ] + } + ], + "actions": [ + { + "type": { + "value": "EffectCapability::EffectBehavior::EnableEffect" + }, + "parameters": [ + "Object", + "Effect", + "EffectName", + "yes" + ] + } + ] + } + ] + } + ], + "parameters": [] + } + ], + "parameters": [ + { + "description": "Object", + "name": "Object", + "type": "object" + }, + { + "description": "Behavior", + "name": "Behavior", + "supplementaryInformation": "ButtonStates::ButtonObjectEffectTween", + "type": "behavior" + } + ], + "objectGroups": [] + }, + { + "fullName": "Time delta", + "functionType": "Expression", + "name": "TimeDelta", + "private": true, + "sentence": "", + "events": [ + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [], + "actions": [ + { + "type": { + "value": "SetReturnNumber" + }, + "parameters": [ + "TimeDelta() * LayerTimeScale(Object.Layer())" + ] + } + ] + } + ], + "expressionType": { + "type": "expression" + }, + "parameters": [ + { + "description": "Object", + "name": "Object", + "type": "object" + }, + { + "description": "Behavior", + "name": "Behavior", + "supplementaryInformation": "ButtonStates::ButtonObjectEffectTween", + "type": "behavior" + } + ], + "objectGroups": [] + }, + { + "fullName": "Fade in", + "functionType": "Action", + "name": "FadeIn", + "private": true, + "sentence": "_PARAM0_ fade in to _PARAM2_", + "events": [ + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [], + "actions": [ + { + "type": { + "value": "SetNumberVariable" + }, + "parameters": [ + "TweenTime", + "=", + "0" + ] + }, + { + "type": { + "value": "SetStringVariable" + }, + "parameters": [ + "TweenState", + "=", + "\"FadeIn\"" + ] + }, + { + "type": { + "value": "SetNumberVariable" + }, + "parameters": [ + "TweenInitialValue", + "=", + "EffectValue" + ] + }, + { + "type": { + "value": "SetNumberVariable" + }, + "parameters": [ + "TweenTargetedValue", + "=", + "Value" + ] + } + ] + } + ], + "parameters": [ + { + "description": "Object", + "name": "Object", + "type": "object" + }, + { + "description": "Behavior", + "name": "Behavior", + "supplementaryInformation": "ButtonStates::ButtonObjectEffectTween", + "type": "behavior" + }, + { + "description": "Value", + "name": "Value", + "type": "expression" + } + ], + "objectGroups": [] + }, + { + "fullName": "Fade out", + "functionType": "Action", + "name": "FadeOut", + "private": true, + "sentence": "_PARAM0_ fade out to _PARAM2_", + "events": [ + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [], + "actions": [ + { + "type": { + "value": "SetNumberVariable" + }, + "parameters": [ + "TweenTime", + "=", + "0" + ] + }, + { + "type": { + "value": "SetStringVariable" + }, + "parameters": [ + "TweenState", + "=", + "\"FadeOut\"" + ] + }, + { + "type": { + "value": "SetNumberVariable" + }, + "parameters": [ + "TweenInitialValue", + "=", + "EffectValue" + ] + }, + { + "type": { + "value": "SetNumberVariable" + }, + "parameters": [ + "TweenTargetedValue", + "=", + "Value" + ] + } + ] + } + ], + "parameters": [ + { + "description": "Object", + "name": "Object", + "type": "object" + }, + { + "description": "Behavior", + "name": "Behavior", + "supplementaryInformation": "ButtonStates::ButtonObjectEffectTween", + "type": "behavior" + }, + { + "description": "Value", + "name": "Value", + "type": "expression" + } + ], + "objectGroups": [] + }, + { + "fullName": "Play tween", + "functionType": "Action", + "name": "PlayTween", + "private": true, + "sentence": "Tween the effect property of object _PARAM0_ over _PARAM2_ seconds with _PARAM3_ easing", + "events": [ + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [ + { + "type": { + "value": "NumberVariable" + }, + "parameters": [ + "TweenTime", + "<", + "Duration" + ] + } + ], + "actions": [ + { + "type": { + "value": "SetNumberVariable" + }, + "parameters": [ + "TweenTime", + "+", + "Object.Behavior::TimeDelta()" + ] + }, + { + "type": { + "value": "SetNumberVariable" + }, + "parameters": [ + "EffectValue", + "=", + "Tween::Ease(Easing, TweenInitialValue, TweenTargetedValue, TweenTime / Duration)" + ] + } + ] + }, + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [ + { + "type": { + "value": "NumberVariable" + }, + "parameters": [ + "TweenTime", + ">=", + "Duration" + ] + } + ], + "actions": [ + { + "type": { + "value": "SetStringVariable" + }, + "parameters": [ + "TweenState", + "=", + "\"NoTween\"" + ] + }, + { + "type": { + "value": "SetNumberVariable" + }, + "parameters": [ + "EffectValue", + "=", + "TweenTargetedValue" + ] + } + ] + }, + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [], + "actions": [ + { + "type": { + "value": "EffectCapability::EffectBehavior::SetEffectDoubleParameter" + }, + "parameters": [ + "Object", + "Effect", + "EffectName", + "EffectProperty", + "EffectValue" + ] + } + ] + } + ], + "parameters": [ + { + "description": "Object", + "name": "Object", + "type": "object" + }, + { + "description": "Behavior", + "name": "Behavior", + "supplementaryInformation": "ButtonStates::ButtonObjectEffectTween", + "type": "behavior" + }, + { + "description": "Duration (in seconds)", + "name": "Duration", + "type": "expression" + }, + { + "description": "Easing", + "name": "Easing", + "supplementaryInformation": "[]", + "type": "string" + } + ], + "objectGroups": [] + }, + { + "description": "the effect name of the object.", + "fullName": "Effect name", + "functionType": "ExpressionAndCondition", + "group": "Button effect tween configuration", + "name": "EffectName", + "sentence": "the effect name", + "events": [ + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [], + "actions": [ + { + "type": { + "value": "SetReturnString" + }, + "parameters": [ + "EffectName" + ] + } + ] + } + ], + "expressionType": { + "type": "string" + }, + "parameters": [ + { + "description": "Object", + "name": "Object", + "type": "object" + }, + { + "description": "Behavior", + "name": "Behavior", + "supplementaryInformation": "ButtonStates::ButtonObjectEffectTween", + "type": "behavior" + } + ], + "objectGroups": [] + }, + { + "description": "the effect parameter of the object. The effect parameter names can be found in the effects tab with the \"Show parameter names\" action of the drop down menu.", + "fullName": "Effect parameter", + "functionType": "ExpressionAndCondition", + "group": "Button effect tween configuration", + "name": "EffectProperty", + "sentence": "the effect parameter", + "events": [ + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [], + "actions": [ + { + "type": { + "value": "SetReturnString" + }, + "parameters": [ + "EffectProperty" + ] + } + ] + } + ], + "expressionType": { + "type": "string" + }, + "parameters": [ + { + "description": "Object", + "name": "Object", + "type": "object" + }, + { + "description": "Behavior", + "name": "Behavior", + "supplementaryInformation": "ButtonStates::ButtonObjectEffectTween", + "type": "behavior" + } + ], + "objectGroups": [] + }, + { + "description": "Change the effect parameter of the object. The effect parameter names can be found in the effects tab with the \"Show parameter names\" action of the drop down menu.", + "fullName": "Effect parameter", + "functionType": "Action", + "getterName": "EffectName", + "group": "Button effect tween configuration", + "name": "SetEffectProperty", + "sentence": "Change the tweened effect of _PARAM0_ to _PARAM2_ with parameter _PARAM3_", + "events": [ + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [], + "actions": [ + { + "type": { + "value": "SetStringVariable" + }, + "parameters": [ + "EffectName", + "=", + "NewEffectName" + ] + }, + { + "type": { + "value": "SetStringVariable" + }, + "parameters": [ + "EffectProperty", + "=", + "NewPropertyName" + ] + } + ] + } + ], + "parameters": [ + { + "description": "Object", + "name": "Object", + "type": "object" + }, + { + "description": "Behavior", + "name": "Behavior", + "supplementaryInformation": "ButtonStates::ButtonObjectEffectTween", + "type": "behavior" + }, + { + "description": "Effect name", + "name": "NewEffectName", + "type": "string" + }, + { + "description": "Parameter name", + "name": "NewPropertyName", + "type": "string" + } + ], + "objectGroups": [] + }, + { + "description": "the idle effect parameter value of the object.", + "fullName": "Idle effect parameter value", + "functionType": "ExpressionAndCondition", + "group": "Button effect tween configuration", + "name": "IdleValue", + "sentence": "the idle effect parameter value", + "events": [ + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [], + "actions": [ + { + "type": { + "value": "SetReturnNumber" + }, + "parameters": [ + "IdleValue" + ] + } + ] + } + ], + "expressionType": { + "type": "expression" + }, + "parameters": [ + { + "description": "Object", + "name": "Object", + "type": "object" + }, + { + "description": "Behavior", + "name": "Behavior", + "supplementaryInformation": "ButtonStates::ButtonObjectEffectTween", + "type": "behavior" + } + ], + "objectGroups": [] + }, + { + "fullName": "", + "functionType": "ActionWithOperator", + "getterName": "IdleValue", + "group": "Button effect tween configuration", + "name": "SetIdleValue", + "sentence": "", + "events": [ + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [], + "actions": [ + { + "type": { + "value": "SetNumberVariable" + }, + "parameters": [ + "IdleValue", + "=", + "Value" + ] + } + ] + } + ], + "parameters": [ + { + "description": "Object", + "name": "Object", + "type": "object" + }, + { + "description": "Behavior", + "name": "Behavior", + "supplementaryInformation": "ButtonStates::ButtonObjectEffectTween", + "type": "behavior" + } + ], + "objectGroups": [] + }, + { + "description": "the focused effect parameter value of the object. The state is Focused when the button is hovered or held outside.", + "fullName": "Focused effect parameter value", + "functionType": "ExpressionAndCondition", + "group": "Button effect tween configuration", + "name": "FocusedValue", + "sentence": "the focused effect parameter value", + "events": [ + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [], + "actions": [ + { + "type": { + "value": "SetReturnNumber" + }, + "parameters": [ + "FocusedValue" + ] + } + ] + } + ], + "expressionType": { + "type": "expression" + }, + "parameters": [ + { + "description": "Object", + "name": "Object", + "type": "object" + }, + { + "description": "Behavior", + "name": "Behavior", + "supplementaryInformation": "ButtonStates::ButtonObjectEffectTween", + "type": "behavior" + } + ], + "objectGroups": [] + }, + { + "fullName": "", + "functionType": "ActionWithOperator", + "getterName": "FocusedValue", + "group": "Button effect tween configuration", + "name": "SetFocusedValue", + "sentence": "", + "events": [ + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [], + "actions": [ + { + "type": { + "value": "SetNumberVariable" + }, + "parameters": [ + "FocusedValue", + "=", + "Value" + ] + } + ] + } + ], + "parameters": [ + { + "description": "Object", + "name": "Object", + "type": "object" + }, + { + "description": "Behavior", + "name": "Behavior", + "supplementaryInformation": "ButtonStates::ButtonObjectEffectTween", + "type": "behavior" + } + ], + "objectGroups": [] + }, + { + "description": "the pressed effect parameter value of the object.", + "fullName": "Pressed effect parameter value", + "functionType": "ExpressionAndCondition", + "group": "Button effect tween configuration", + "name": "PressedValue", + "sentence": "the pressed effect parameter value", + "events": [ + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [], + "actions": [ + { + "type": { + "value": "SetReturnNumber" + }, + "parameters": [ + "PressedValue" + ] + } + ] + } + ], + "expressionType": { + "type": "expression" + }, + "parameters": [ + { + "description": "Object", + "name": "Object", + "type": "object" + }, + { + "description": "Behavior", + "name": "Behavior", + "supplementaryInformation": "ButtonStates::ButtonObjectEffectTween", + "type": "behavior" + } + ], + "objectGroups": [] + }, + { + "fullName": "", + "functionType": "ActionWithOperator", + "getterName": "PressedValue", + "group": "Button effect tween configuration", + "name": "SetPressedValue", + "sentence": "", + "events": [ + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [], + "actions": [ + { + "type": { + "value": "SetNumberVariable" + }, + "parameters": [ + "PressedValue", + "=", + "Value" + ] + } + ] + } + ], + "parameters": [ + { + "description": "Object", + "name": "Object", + "type": "object" + }, + { + "description": "Behavior", + "name": "Behavior", + "supplementaryInformation": "ButtonStates::ButtonObjectEffectTween", + "type": "behavior" + } + ], + "objectGroups": [] + }, + { + "description": "the fade-in easing of the object.", + "fullName": "Fade-in easing", + "functionType": "ExpressionAndCondition", + "group": "Button effect tween configuration", + "name": "FadeInEasing", + "sentence": "the fade-in easing", + "events": [ + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [], + "actions": [ + { + "type": { + "value": "SetReturnString" + }, + "parameters": [ + "FadeInEasing" + ] + } + ] + } + ], + "expressionType": { + "supplementaryInformation": "[\"linear\",\"easeInQuad\",\"easeOutQuad\",\"easeInOutQuad\",\"easeInCubic\",\"easeOutCubic\",\"easeInOutCubic\",\"easeInQuart\",\"easeOutQuart\",\"easeInOutQuart\",\"easeInQuint\",\"easeOutQuint\",\"easeInOutQuint\",\"easeInOutSine\",\"easeInExpo\",\"easeOutExpo\",\"easeInOutExpo\",\"easeInCirc\",\"easeOutCirc\",\"easeInOutCirc\",\"easeOutBounce\",\"easeInBack\",\"easeOutBack\",\"easeInOutBack\",\"elastic\",\"swingFromTo\",\"swingFrom\",\"swingTo\",\"bounce\",\"bouncePast\",\"easeFromTo\",\"easeFrom\",\"easeTo\"]", + "type": "stringWithSelector" + }, + "parameters": [ + { + "description": "Object", + "name": "Object", + "type": "object" + }, + { + "description": "Behavior", + "name": "Behavior", + "supplementaryInformation": "ButtonStates::ButtonObjectEffectTween", + "type": "behavior" + } + ], + "objectGroups": [] + }, + { + "fullName": "", + "functionType": "ActionWithOperator", + "getterName": "FadeInEasing", + "group": "Button effect tween configuration", + "name": "SetFadeInEasing", + "sentence": "", + "events": [ + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [], + "actions": [ + { + "type": { + "value": "SetStringVariable" + }, + "parameters": [ + "FadeInEasing", + "=", + "Value" + ] + } + ] + } + ], + "parameters": [ + { + "description": "Object", + "name": "Object", + "type": "object" + }, + { + "description": "Behavior", + "name": "Behavior", + "supplementaryInformation": "ButtonStates::ButtonObjectEffectTween", + "type": "behavior" + } + ], + "objectGroups": [] + }, + { + "description": "the fade-out easing of the object.", + "fullName": "Fade-out easing", + "functionType": "ExpressionAndCondition", + "group": "Button effect tween configuration", + "name": "FadeOutEasing", + "sentence": "the fade-out easing", + "events": [ + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [], + "actions": [ + { + "type": { + "value": "SetReturnString" + }, + "parameters": [ + "FadeOutEasing" + ] + } + ] + } + ], + "expressionType": { + "supplementaryInformation": "[\"linear\",\"easeInQuad\",\"easeOutQuad\",\"easeInOutQuad\",\"easeInCubic\",\"easeOutCubic\",\"easeInOutCubic\",\"easeInQuart\",\"easeOutQuart\",\"easeInOutQuart\",\"easeInQuint\",\"easeOutQuint\",\"easeInOutQuint\",\"easeInOutSine\",\"easeInExpo\",\"easeOutExpo\",\"easeInOutExpo\",\"easeInCirc\",\"easeOutCirc\",\"easeInOutCirc\",\"easeOutBounce\",\"easeInBack\",\"easeOutBack\",\"easeInOutBack\",\"elastic\",\"swingFromTo\",\"swingFrom\",\"swingTo\",\"bounce\",\"bouncePast\",\"easeFromTo\",\"easeFrom\",\"easeTo\"]", + "type": "stringWithSelector" + }, + "parameters": [ + { + "description": "Object", + "name": "Object", + "type": "object" + }, + { + "description": "Behavior", + "name": "Behavior", + "supplementaryInformation": "ButtonStates::ButtonObjectEffectTween", + "type": "behavior" + } + ], + "objectGroups": [] + }, + { + "fullName": "", + "functionType": "ActionWithOperator", + "getterName": "FadeOutEasing", + "group": "Button effect tween configuration", + "name": "SetFadeOutEasing", + "sentence": "", + "events": [ + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [], + "actions": [ + { + "type": { + "value": "SetStringVariable" + }, + "parameters": [ + "FadeOutEasing", + "=", + "Value" + ] + } + ] + } + ], + "parameters": [ + { + "description": "Object", + "name": "Object", + "type": "object" + }, + { + "description": "Behavior", + "name": "Behavior", + "supplementaryInformation": "ButtonStates::ButtonObjectEffectTween", + "type": "behavior" + } + ], + "objectGroups": [] + }, + { + "description": "the fade-in duration of the object.", + "fullName": "Fade-in duration", + "functionType": "ExpressionAndCondition", + "group": "Button effect tween configuration", + "name": "FadeInDuration", + "sentence": "the fade-in duration", + "events": [ + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [], + "actions": [ + { + "type": { + "value": "SetReturnNumber" + }, + "parameters": [ + "FadeInDuration" + ] + } + ] + } + ], + "expressionType": { + "type": "expression" + }, + "parameters": [ + { + "description": "Object", + "name": "Object", + "type": "object" + }, + { + "description": "Behavior", + "name": "Behavior", + "supplementaryInformation": "ButtonStates::ButtonObjectEffectTween", + "type": "behavior" + } + ], + "objectGroups": [] + }, + { + "fullName": "", + "functionType": "ActionWithOperator", + "getterName": "FadeInDuration", + "group": "Button effect tween configuration", + "name": "SetFadeInDuration", + "sentence": "", + "events": [ + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [], + "actions": [ + { + "type": { + "value": "SetNumberVariable" + }, + "parameters": [ + "FadeInDuration", + "=", + "Value" + ] + } + ] + } + ], + "parameters": [ + { + "description": "Object", + "name": "Object", + "type": "object" + }, + { + "description": "Behavior", + "name": "Behavior", + "supplementaryInformation": "ButtonStates::ButtonObjectEffectTween", + "type": "behavior" + } + ], + "objectGroups": [] + }, + { + "description": "the fade-out duration of the object.", + "fullName": "Fade-out duration", + "functionType": "ExpressionAndCondition", + "group": "Button effect tween configuration", + "name": "FadeOutDuration", + "sentence": "the fade-out duration", + "events": [ + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [], + "actions": [ + { + "type": { + "value": "SetReturnNumber" + }, + "parameters": [ + "FadeOutDuration" + ] + } + ] + } + ], + "expressionType": { + "type": "expression" + }, + "parameters": [ + { + "description": "Object", + "name": "Object", + "type": "object" + }, + { + "description": "Behavior", + "name": "Behavior", + "supplementaryInformation": "ButtonStates::ButtonObjectEffectTween", + "type": "behavior" + } + ], + "objectGroups": [] + }, + { + "fullName": "", + "functionType": "ActionWithOperator", + "getterName": "FadeOutDuration", + "group": "Button effect tween configuration", + "name": "SetFadeOutDuration", + "sentence": "", + "events": [ + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [], + "actions": [ + { + "type": { + "value": "SetNumberVariable" + }, + "parameters": [ + "FadeOutDuration", + "=", + "Value" + ] + } + ] + } + ], + "parameters": [ + { + "description": "Object", + "name": "Object", + "type": "object" + }, + { + "description": "Behavior", + "name": "Behavior", + "supplementaryInformation": "ButtonStates::ButtonObjectEffectTween", + "type": "behavior" + } + ], + "objectGroups": [] + } + ], + "eventsFunctionsFolderStructure": { + "folderName": "__ROOT", + "children": [ + { + "functionName": "onCreated" + }, + { + "functionName": "doStepPreEvents" + }, + { + "functionName": "TimeDelta" + }, + { + "functionName": "FadeIn" + }, + { + "functionName": "FadeOut" + }, + { + "functionName": "PlayTween" + }, + { + "folderName": "Button effect tween configuration", + "children": [ + { + "functionName": "EffectName" + }, + { + "functionName": "EffectProperty" + }, + { + "functionName": "SetEffectProperty" + }, + { + "functionName": "IdleValue" + }, + { + "functionName": "SetIdleValue" + }, + { + "functionName": "FocusedValue" + }, + { + "functionName": "SetFocusedValue" + }, + { + "functionName": "PressedValue" + }, + { + "functionName": "SetPressedValue" + }, + { + "functionName": "FadeInEasing" + }, + { + "functionName": "SetFadeInEasing" + }, + { + "functionName": "FadeOutEasing" + }, + { + "functionName": "SetFadeOutEasing" + }, + { + "functionName": "FadeInDuration" + }, + { + "functionName": "SetFadeInDuration" + }, + { + "functionName": "FadeOutDuration" + }, + { + "functionName": "SetFadeOutDuration" + } + ] + } + ] + }, + "propertyDescriptors": [ + { + "value": "", + "type": "Behavior", + "label": "Button states", + "extraInformation": [ + "ButtonStates::ButtonFSM" + ], + "choices": [], + "name": "ButtonFSM" + }, + { + "value": "", + "type": "Behavior", + "label": "Effect capability", + "extraInformation": [ + "EffectCapability::EffectBehavior" + ], + "choices": [], + "name": "Effect" + }, + { + "value": "Effect", + "type": "String", + "label": "Effect name", + "group": "Effect", + "name": "EffectName" + }, + { + "value": "", + "type": "String", + "label": "Effect parameter", + "description": "The effect parameter names can be found in the effects tab with the \"Show parameter names\" action of the drop down menu.", + "group": "Effect", + "name": "EffectProperty" + }, + { + "value": "0", + "type": "Number", + "label": "Idle effect parameter value", + "group": "Value", + "name": "IdleValue" + }, + { + "value": "0", + "type": "Number", + "label": "Focused effect parameter value", + "description": "The state is Focused when the button is hovered or held outside.", + "group": "Value", + "name": "FocusedValue" + }, + { + "value": "0", + "type": "Number", + "label": "Pressed effect parameter value", + "group": "Value", + "name": "PressedValue" + }, + { + "value": "easeInOutQuad", + "type": "Choice", + "label": "Fade-in easing", + "group": "Speed", + "choices": [ + { + "label": "linear", + "value": "linear" + }, + { + "label": "easeInQuad", + "value": "easeInQuad" + }, + { + "label": "easeOutQuad", + "value": "easeOutQuad" + }, + { + "label": "easeInOutQuad", + "value": "easeInOutQuad" + }, + { + "label": "easeInCubic", + "value": "easeInCubic" + }, + { + "label": "easeOutCubic", + "value": "easeOutCubic" + }, + { + "label": "easeInOutCubic", + "value": "easeInOutCubic" + }, + { + "label": "easeInQuart", + "value": "easeInQuart" + }, + { + "label": "easeOutQuart", + "value": "easeOutQuart" + }, + { + "label": "easeInOutQuart", + "value": "easeInOutQuart" + }, + { + "label": "easeInQuint", + "value": "easeInQuint" + }, + { + "label": "easeOutQuint", + "value": "easeOutQuint" + }, + { + "label": "easeInOutQuint", + "value": "easeInOutQuint" + }, + { + "label": "easeInOutSine", + "value": "easeInOutSine" + }, + { + "label": "easeInExpo", + "value": "easeInExpo" + }, + { + "label": "easeOutExpo", + "value": "easeOutExpo" + }, + { + "label": "easeInOutExpo", + "value": "easeInOutExpo" + }, + { + "label": "easeInCirc", + "value": "easeInCirc" + }, + { + "label": "easeOutCirc", + "value": "easeOutCirc" + }, + { + "label": "easeInOutCirc", + "value": "easeInOutCirc" + }, + { + "label": "easeOutBounce", + "value": "easeOutBounce" + }, + { + "label": "easeInBack", + "value": "easeInBack" + }, + { + "label": "easeOutBack", + "value": "easeOutBack" + }, + { + "label": "easeInOutBack", + "value": "easeInOutBack" + }, + { + "label": "elastic", + "value": "elastic" + }, + { + "label": "swingFromTo", + "value": "swingFromTo" + }, + { + "label": "swingFrom", + "value": "swingFrom" + }, + { + "label": "swingTo", + "value": "swingTo" + }, + { + "label": "bounce", + "value": "bounce" + }, + { + "label": "bouncePast", + "value": "bouncePast" + }, + { + "label": "easeFromTo", + "value": "easeFromTo" + }, + { + "label": "easeFrom", + "value": "easeFrom" + }, + { + "label": "easeTo", + "value": "easeTo" + } + ], + "name": "FadeInEasing" + }, + { + "value": "easeInOutQuad", + "type": "Choice", + "label": "Fade-out easing", + "group": "Speed", + "choices": [ + { + "label": "linear", + "value": "linear" + }, + { + "label": "easeInQuad", + "value": "easeInQuad" + }, + { + "label": "easeOutQuad", + "value": "easeOutQuad" + }, + { + "label": "easeInOutQuad", + "value": "easeInOutQuad" + }, + { + "label": "easeInCubic", + "value": "easeInCubic" + }, + { + "label": "easeOutCubic", + "value": "easeOutCubic" + }, + { + "label": "easeInOutCubic", + "value": "easeInOutCubic" + }, + { + "label": "easeInQuart", + "value": "easeInQuart" + }, + { + "label": "easeOutQuart", + "value": "easeOutQuart" + }, + { + "label": "easeInOutQuart", + "value": "easeInOutQuart" + }, + { + "label": "easeInQuint", + "value": "easeInQuint" + }, + { + "label": "easeOutQuint", + "value": "easeOutQuint" + }, + { + "label": "easeInOutQuint", + "value": "easeInOutQuint" + }, + { + "label": "easeInOutSine", + "value": "easeInOutSine" + }, + { + "label": "easeInExpo", + "value": "easeInExpo" + }, + { + "label": "easeOutExpo", + "value": "easeOutExpo" + }, + { + "label": "easeInOutExpo", + "value": "easeInOutExpo" + }, + { + "label": "easeInCirc", + "value": "easeInCirc" + }, + { + "label": "easeOutCirc", + "value": "easeOutCirc" + }, + { + "label": "easeInOutCirc", + "value": "easeInOutCirc" + }, + { + "label": "easeOutBounce", + "value": "easeOutBounce" + }, + { + "label": "easeInBack", + "value": "easeInBack" + }, + { + "label": "easeOutBack", + "value": "easeOutBack" + }, + { + "label": "easeInOutBack", + "value": "easeInOutBack" + }, + { + "label": "elastic", + "value": "elastic" + }, + { + "label": "swingFromTo", + "value": "swingFromTo" + }, + { + "label": "swingFrom", + "value": "swingFrom" + }, + { + "label": "swingTo", + "value": "swingTo" + }, + { + "label": "bounce", + "value": "bounce" + }, + { + "label": "bouncePast", + "value": "bouncePast" + }, + { + "label": "easeFromTo", + "value": "easeFromTo" + }, + { + "label": "easeFrom", + "value": "easeFrom" + }, + { + "label": "easeTo", + "value": "easeTo" + } + ], + "name": "FadeOutEasing" + }, + { + "value": "0.125", + "type": "Number", + "unit": "Second", + "label": "Fade-in duration", + "group": "Speed", + "name": "FadeInDuration" + }, + { + "value": "0.5", + "type": "Number", + "unit": "Second", + "label": "Fade-out duration", + "group": "Speed", + "name": "FadeOutDuration" + }, + { + "value": "", + "type": "Boolean", + "label": "Disable the effect in idle state", + "advanced": true, + "name": "ShouldDisableEffectInIdle" + }, + { + "value": "Idle", + "type": "Choice", + "label": "", + "choices": [ + { + "label": "Idle", + "value": "Idle" + }, + { + "label": "Focused", + "value": "Focused" + }, + { + "label": "Pressed", + "value": "Pressed" + } + ], + "hidden": true, + "name": "PreviousState" + }, + { + "value": "0", + "type": "Number", + "label": "", + "hidden": true, + "name": "TweenInitialValue" + }, + { + "value": "0", + "type": "Number", + "label": "", + "hidden": true, + "name": "TweenTargetedValue" + }, + { + "value": "0", + "type": "Number", + "label": "", + "hidden": true, + "name": "TweenTime" + }, + { + "value": "NoTween", + "type": "Choice", + "label": "", + "choices": [ + { + "label": "NoTween", + "value": "NoTween" + }, + { + "label": "FadeIn", + "value": "FadeIn" + }, + { + "label": "FadeOut", + "value": "FadeOut" + } + ], + "hidden": true, + "name": "TweenState" + }, + { + "value": "0", + "type": "Number", + "label": "", + "hidden": true, + "name": "EffectValue" + } + ], + "propertiesFolderStructure": { + "folderName": "__ROOT", + "children": [ + { + "propertyName": "ButtonFSM" + }, + { + "propertyName": "Effect" + }, + { + "folderName": "Effect", + "children": [ + { + "propertyName": "EffectName" + }, + { + "propertyName": "EffectProperty" + } + ] + }, + { + "folderName": "Value", + "children": [ + { + "propertyName": "IdleValue" + }, + { + "propertyName": "FocusedValue" + }, + { + "propertyName": "PressedValue" + } + ] + }, + { + "folderName": "Speed", + "children": [ + { + "propertyName": "FadeInEasing" + }, + { + "propertyName": "FadeOutEasing" + }, + { + "propertyName": "FadeInDuration" + }, + { + "propertyName": "FadeOutDuration" + } + ] + }, + { + "propertyName": "ShouldDisableEffectInIdle" + }, + { + "propertyName": "PreviousState" + }, + { + "propertyName": "TweenInitialValue" + }, + { + "propertyName": "TweenTargetedValue" + }, + { + "propertyName": "TweenTime" + }, + { + "propertyName": "TweenState" + }, + { + "propertyName": "EffectValue" + } + ] + } + }, + { + "description": "Smoothly resize buttons according to their state.", + "fullName": "Button scale tween", + "helpPath": "", + "iconUrl": "", + "name": "ButtonScaleTween", + "objectType": "", + "previewIconUrl": "", + "eventsFunctions": [ + { + "fullName": "", + "functionType": "Action", + "name": "onCreated", + "sentence": "", + "events": [ + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [], + "actions": [ + { + "type": { + "value": "ScalableCapability::ScalableBehavior::SetValue" + }, + "parameters": [ + "Object", + "Scale", + "=", + "IdleScale" + ] + } + ] + } + ], + "parameters": [ + { + "description": "Object", + "name": "Object", + "type": "object" + }, + { + "description": "Behavior", + "name": "Behavior", + "supplementaryInformation": "ButtonStates::ButtonScaleTween", + "type": "behavior" + } + ], + "objectGroups": [] + }, + { + "fullName": "", + "functionType": "Action", + "name": "doStepPreEvents", + "sentence": "", + "events": [ + { + "colorB": 228, + "colorG": 176, + "colorR": 74, + "creationTime": 0, + "name": "Change based on state", + "source": "", + "type": "BuiltinCommonInstructions::Group", + "events": [ + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [ + { + "type": { + "value": "ButtonStates::ButtonFSM::IsIdle" + }, + "parameters": [ + "Object", + "ButtonFSM", + "" + ] + }, + { + "type": { + "value": "StringVariable" + }, + "parameters": [ + "PreviousState", + "!=", + "\"Idle\"" + ] + } + ], + "actions": [ + { + "type": { + "value": "SetStringVariable" + }, + "parameters": [ + "PreviousState", + "=", + "\"Idle\"" + ] + }, + { + "type": { + "value": "ButtonStates::ButtonScaleTween::FadeOut" + }, + "parameters": [ + "Object", + "Behavior", + "IdleScale", + "" + ] + } + ] + }, + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [ + { + "type": { + "value": "ButtonStates::ButtonFSM::IsFocused" + }, + "parameters": [ + "Object", + "ButtonFSM", + "" + ] + } + ], + "actions": [], + "events": [ + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [ + { + "type": { + "value": "StringVariable" + }, + "parameters": [ + "PreviousState", + "=", + "\"Idle\"" + ] + } + ], + "actions": [ + { + "type": { + "value": "SetStringVariable" + }, + "parameters": [ + "PreviousState", + "=", + "\"Focused\"" + ] + }, + { + "type": { + "value": "ButtonStates::ButtonScaleTween::FadeIn" + }, + "parameters": [ + "Object", + "Behavior", + "FocusedScale", + "" + ] + } + ] + }, + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [ + { + "type": { + "value": "StringVariable" + }, + "parameters": [ + "PreviousState", + "=", + "\"Pressed\"" + ] + } + ], + "actions": [ + { + "type": { + "value": "SetStringVariable" + }, + "parameters": [ + "PreviousState", + "=", + "\"Focused\"" + ] + }, + { + "type": { + "value": "ButtonStates::ButtonScaleTween::FadeOut" + }, + "parameters": [ + "Object", + "Behavior", + "FocusedScale", + "" + ] + } + ] + } + ] + }, + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [ + { + "type": { + "value": "ButtonStates::ButtonFSM::IsPressed" + }, + "parameters": [ + "Object", + "ButtonFSM", + "" + ] + }, + { + "type": { + "value": "StringVariable" + }, + "parameters": [ + "PreviousState", + "!=", + "\"Pressed\"" + ] + } + ], + "actions": [ + { + "type": { + "value": "SetStringVariable" + }, + "parameters": [ + "PreviousState", + "=", + "\"Pressed\"" + ] + }, + { + "type": { + "value": "ButtonStates::ButtonScaleTween::FadeIn" + }, + "parameters": [ + "Object", + "Behavior", + "PressedScale", + "" + ] + } + ] + } + ], + "parameters": [] + } + ], + "parameters": [ + { + "description": "Object", + "name": "Object", + "type": "object" + }, + { + "description": "Behavior", + "name": "Behavior", + "supplementaryInformation": "ButtonStates::ButtonScaleTween", + "type": "behavior" + } + ], + "objectGroups": [] + }, + { + "fullName": "Fade in", + "functionType": "Action", + "name": "FadeIn", + "private": true, + "sentence": "_PARAM0_ fade in to _PARAM2_", + "events": [ + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [], + "actions": [ + { + "type": { + "value": "Tween::AddObjectScaleTween" + }, + "parameters": [ + "Object", + "Tween", + "\"__ButtonScaleTween.Fade\"", + "Value", + "Value", + "FadeInEasing", + "1000 * FadeInDuration", + "no", + "yes" + ] + } + ] + } + ], + "parameters": [ + { + "description": "Object", + "name": "Object", + "type": "object" + }, + { + "description": "Behavior", + "name": "Behavior", + "supplementaryInformation": "ButtonStates::ButtonScaleTween", + "type": "behavior" + }, + { + "description": "Value", + "name": "Value", + "type": "expression" + } + ], + "objectGroups": [] + }, + { + "fullName": "Fade out", + "functionType": "Action", + "name": "FadeOut", + "private": true, + "sentence": "_PARAM0_ fade out to _PARAM2_", + "events": [ + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [], + "actions": [ + { + "type": { + "value": "Tween::AddObjectScaleTween" + }, + "parameters": [ + "Object", + "Tween", + "\"__ButtonScaleTween.Fade\"", + "Value", + "Value", + "FadeOutEasing", + "1000 * FadeOutDuration", + "no", + "yes" + ] + } + ] + } + ], + "parameters": [ + { + "description": "Object", + "name": "Object", + "type": "object" + }, + { + "description": "Behavior", + "name": "Behavior", + "supplementaryInformation": "ButtonStates::ButtonScaleTween", + "type": "behavior" + }, + { + "description": "Value", + "name": "Value", + "type": "expression" + } + ], + "objectGroups": [] + }, + { + "description": "the idle state size scale of the object.", + "fullName": "Idle state size scale", + "functionType": "ExpressionAndCondition", + "group": "Button scale tween configuration", + "name": "IdleScale", + "sentence": "the idle state size scale", + "events": [ + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [], + "actions": [ + { + "type": { + "value": "SetReturnNumber" + }, + "parameters": [ + "IdleScale" + ] + } + ] + } + ], + "expressionType": { + "type": "expression" + }, + "parameters": [ + { + "description": "Object", + "name": "Object", + "type": "object" + }, + { + "description": "Behavior", + "name": "Behavior", + "supplementaryInformation": "ButtonStates::ButtonScaleTween", + "type": "behavior" + } + ], + "objectGroups": [] + }, + { + "fullName": "", + "functionType": "ActionWithOperator", + "getterName": "IdleScale", + "group": "Button scale tween configuration", + "name": "SetIdleScale", + "sentence": "", + "events": [ + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [], + "actions": [ + { + "type": { + "value": "SetNumberVariable" + }, + "parameters": [ + "IdleScale", + "=", + "Value" + ] + } + ] + } + ], + "parameters": [ + { + "description": "Object", + "name": "Object", + "type": "object" + }, + { + "description": "Behavior", + "name": "Behavior", + "supplementaryInformation": "ButtonStates::ButtonScaleTween", + "type": "behavior" + } + ], + "objectGroups": [] + }, + { + "description": "the focused state size scale of the object. The state is Focused when the button is hovered or held outside.", + "fullName": "Focused state size scale", + "functionType": "ExpressionAndCondition", + "group": "Button scale tween configuration", + "name": "FocusedScale", + "sentence": "the focused state size scale", + "events": [ + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [], + "actions": [ + { + "type": { + "value": "SetReturnNumber" + }, + "parameters": [ + "FocusedScale" + ] + } + ] + } + ], + "expressionType": { + "type": "expression" + }, + "parameters": [ + { + "description": "Object", + "name": "Object", + "type": "object" + }, + { + "description": "Behavior", + "name": "Behavior", + "supplementaryInformation": "ButtonStates::ButtonScaleTween", + "type": "behavior" + } + ], + "objectGroups": [] + }, + { + "fullName": "", + "functionType": "ActionWithOperator", + "getterName": "FocusedScale", + "group": "Button scale tween configuration", + "name": "SetFocusedScale", + "sentence": "", + "events": [ + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [], + "actions": [ + { + "type": { + "value": "SetNumberVariable" + }, + "parameters": [ + "FocusedScale", + "=", + "Value" + ] + } + ] + } + ], + "parameters": [ + { + "description": "Object", + "name": "Object", + "type": "object" + }, + { + "description": "Behavior", + "name": "Behavior", + "supplementaryInformation": "ButtonStates::ButtonScaleTween", + "type": "behavior" + } + ], + "objectGroups": [] + }, + { + "description": "the pressed state size scale of the object.", + "fullName": "Pressed state size scale", + "functionType": "ExpressionAndCondition", + "group": "Button scale tween configuration", + "name": "PressedScale", + "sentence": "the pressed state size scale", + "events": [ + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [], + "actions": [ + { + "type": { + "value": "SetReturnNumber" + }, + "parameters": [ + "PressedScale" + ] + } + ] + } + ], + "expressionType": { + "type": "expression" + }, + "parameters": [ + { + "description": "Object", + "name": "Object", + "type": "object" + }, + { + "description": "Behavior", + "name": "Behavior", + "supplementaryInformation": "ButtonStates::ButtonScaleTween", + "type": "behavior" + } + ], + "objectGroups": [] + }, + { + "fullName": "", + "functionType": "ActionWithOperator", + "getterName": "PressedScale", + "group": "Button scale tween configuration", + "name": "SetPressedScale", + "sentence": "", + "events": [ + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [], + "actions": [ + { + "type": { + "value": "SetNumberVariable" + }, + "parameters": [ + "PressedScale", + "=", + "Value" + ] + } + ] + } + ], + "parameters": [ + { + "description": "Object", + "name": "Object", + "type": "object" + }, + { + "description": "Behavior", + "name": "Behavior", + "supplementaryInformation": "ButtonStates::ButtonScaleTween", + "type": "behavior" + } + ], + "objectGroups": [] + }, + { + "description": "the fade-in duration of the object.", + "fullName": "Fade-in duration", + "functionType": "ExpressionAndCondition", + "group": "Button scale tween configuration", + "name": "FadeInDuration", + "sentence": "the fade-in duration", + "events": [ + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [], + "actions": [ + { + "type": { + "value": "SetReturnNumber" + }, + "parameters": [ + "FadeInDuration" + ] + } + ] + } + ], + "expressionType": { + "type": "expression" + }, + "parameters": [ + { + "description": "Object", + "name": "Object", + "type": "object" + }, + { + "description": "Behavior", + "name": "Behavior", + "supplementaryInformation": "ButtonStates::ButtonScaleTween", + "type": "behavior" + } + ], + "objectGroups": [] + }, + { + "fullName": "", + "functionType": "ActionWithOperator", + "getterName": "FadeInDuration", + "group": "Button scale tween configuration", + "name": "SetFadeInDuration", + "sentence": "", + "events": [ + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [], + "actions": [ + { + "type": { + "value": "SetNumberVariable" + }, + "parameters": [ + "FadeInDuration", + "=", + "Value" + ] + } + ] + } + ], + "parameters": [ + { + "description": "Object", + "name": "Object", + "type": "object" + }, + { + "description": "Behavior", + "name": "Behavior", + "supplementaryInformation": "ButtonStates::ButtonScaleTween", + "type": "behavior" + } + ], + "objectGroups": [] + }, + { + "description": "the fade-out duration of the object.", + "fullName": "Fade-out duration", + "functionType": "ExpressionAndCondition", + "group": "Button scale tween configuration", + "name": "FadeOutDuration", + "sentence": "the fade-out duration", + "events": [ + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [], + "actions": [ + { + "type": { + "value": "SetReturnNumber" + }, + "parameters": [ + "FadeOutDuration" + ] + } + ] + } + ], + "expressionType": { + "type": "expression" + }, + "parameters": [ + { + "description": "Object", + "name": "Object", + "type": "object" + }, + { + "description": "Behavior", + "name": "Behavior", + "supplementaryInformation": "ButtonStates::ButtonScaleTween", + "type": "behavior" + } + ], + "objectGroups": [] + }, + { + "fullName": "", + "functionType": "ActionWithOperator", + "getterName": "FadeOutDuration", + "group": "Button scale tween configuration", + "name": "SetFadeOutDuration", + "sentence": "", + "events": [ + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [], + "actions": [ + { + "type": { + "value": "SetNumberVariable" + }, + "parameters": [ + "FadeOutDuration", + "=", + "Value" + ] + } + ] + } + ], + "parameters": [ + { + "description": "Object", + "name": "Object", + "type": "object" + }, + { + "description": "Behavior", + "name": "Behavior", + "supplementaryInformation": "ButtonStates::ButtonScaleTween", + "type": "behavior" + } + ], + "objectGroups": [] + }, + { + "description": "the fade-in easing of the object.", + "fullName": "Fade-in easing", + "functionType": "ExpressionAndCondition", + "group": "Button scale tween configuration", + "name": "FadeInEasing", + "sentence": "the fade-in easing", + "events": [ + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [], + "actions": [ + { + "type": { + "value": "SetReturnString" + }, + "parameters": [ + "FadeInEasing" + ] + } + ] + } + ], + "expressionType": { + "supplementaryInformation": "[\"linear\",\"easeInQuad\",\"easeOutQuad\",\"easeInOutQuad\",\"easeInCubic\",\"easeOutCubic\",\"easeInOutCubic\",\"easeInQuart\",\"easeOutQuart\",\"easeInOutQuart\",\"easeInQuint\",\"easeOutQuint\",\"easeInOutQuint\",\"easeInOutSine\",\"easeInExpo\",\"easeOutExpo\",\"easeInOutExpo\",\"easeInCirc\",\"easeOutCirc\",\"easeInOutCirc\",\"easeOutBounce\",\"easeInBack\",\"easeOutBack\",\"easeInOutBack\",\"elastic\",\"swingFromTo\",\"swingFrom\",\"swingTo\",\"bounce\",\"bouncePast\",\"easeFromTo\",\"easeFrom\",\"easeTo\"]", + "type": "stringWithSelector" + }, + "parameters": [ + { + "description": "Object", + "name": "Object", + "type": "object" + }, + { + "description": "Behavior", + "name": "Behavior", + "supplementaryInformation": "ButtonStates::ButtonScaleTween", + "type": "behavior" + } + ], + "objectGroups": [] + }, + { + "fullName": "", + "functionType": "ActionWithOperator", + "getterName": "FadeInEasing", + "group": "Button scale tween configuration", + "name": "SetFadeInEasing", + "sentence": "", + "events": [ + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [], + "actions": [ + { + "type": { + "value": "SetStringVariable" + }, + "parameters": [ + "FadeInEasing", + "=", + "Value" + ] + } + ] + } + ], + "parameters": [ + { + "description": "Object", + "name": "Object", + "type": "object" + }, + { + "description": "Behavior", + "name": "Behavior", + "supplementaryInformation": "ButtonStates::ButtonScaleTween", + "type": "behavior" + } + ], + "objectGroups": [] + }, + { + "description": "the fade-out easing of the object.", + "fullName": "Fade-out easing", + "functionType": "ExpressionAndCondition", + "group": "Button scale tween configuration", + "name": "FadeOutEasing", + "sentence": "the fade-out easing", + "events": [ + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [], + "actions": [ + { + "type": { + "value": "SetReturnString" + }, + "parameters": [ + "FadeOutEasing" + ] + } + ] + } + ], + "expressionType": { + "supplementaryInformation": "[\"linear\",\"easeInQuad\",\"easeOutQuad\",\"easeInOutQuad\",\"easeInCubic\",\"easeOutCubic\",\"easeInOutCubic\",\"easeInQuart\",\"easeOutQuart\",\"easeInOutQuart\",\"easeInQuint\",\"easeOutQuint\",\"easeInOutQuint\",\"easeInOutSine\",\"easeInExpo\",\"easeOutExpo\",\"easeInOutExpo\",\"easeInCirc\",\"easeOutCirc\",\"easeInOutCirc\",\"easeOutBounce\",\"easeInBack\",\"easeOutBack\",\"easeInOutBack\",\"elastic\",\"swingFromTo\",\"swingFrom\",\"swingTo\",\"bounce\",\"bouncePast\",\"easeFromTo\",\"easeFrom\",\"easeTo\"]", + "type": "stringWithSelector" + }, + "parameters": [ + { + "description": "Object", + "name": "Object", + "type": "object" + }, + { + "description": "Behavior", + "name": "Behavior", + "supplementaryInformation": "ButtonStates::ButtonScaleTween", + "type": "behavior" + } + ], + "objectGroups": [] + }, + { + "fullName": "", + "functionType": "ActionWithOperator", + "getterName": "FadeOutEasing", + "group": "Button scale tween configuration", + "name": "SetFadeOutEasing", + "sentence": "", + "events": [ + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [], + "actions": [ + { + "type": { + "value": "SetStringVariable" + }, + "parameters": [ + "FadeOutEasing", + "=", + "Value" + ] + } + ] + } + ], + "parameters": [ + { + "description": "Object", + "name": "Object", + "type": "object" + }, + { + "description": "Behavior", + "name": "Behavior", + "supplementaryInformation": "ButtonStates::ButtonScaleTween", + "type": "behavior" + } + ], + "objectGroups": [] + } + ], + "eventsFunctionsFolderStructure": { + "folderName": "__ROOT", + "children": [ + { + "functionName": "onCreated" + }, + { + "functionName": "doStepPreEvents" + }, + { + "functionName": "FadeIn" + }, + { + "functionName": "FadeOut" + }, + { + "folderName": "Button scale tween configuration", + "children": [ + { + "functionName": "IdleScale" + }, + { + "functionName": "SetIdleScale" + }, + { + "functionName": "FocusedScale" + }, + { + "functionName": "SetFocusedScale" + }, + { + "functionName": "PressedScale" + }, + { + "functionName": "SetPressedScale" + }, + { + "functionName": "FadeInDuration" + }, + { + "functionName": "SetFadeInDuration" + }, + { + "functionName": "FadeOutDuration" + }, + { + "functionName": "SetFadeOutDuration" + }, + { + "functionName": "FadeInEasing" + }, + { + "functionName": "SetFadeInEasing" + }, + { + "functionName": "FadeOutEasing" + }, + { + "functionName": "SetFadeOutEasing" + } + ] + } + ] + }, + "propertyDescriptors": [ + { + "value": "", + "type": "Behavior", + "label": "Scalable capability", + "extraInformation": [ + "ScalableCapability::ScalableBehavior" + ], + "choices": [], + "name": "Scale" + }, + { + "value": "", + "type": "Behavior", + "label": "Button states behavior (required)", + "extraInformation": [ + "ButtonStates::ButtonFSM" + ], + "choices": [], + "name": "ButtonFSM" + }, + { + "value": "", + "type": "Behavior", + "label": "Tween behavior (required)", + "extraInformation": [ + "Tween::TweenBehavior" + ], + "choices": [], + "name": "Tween" + }, + { + "value": "1", + "type": "Number", + "unit": "Dimensionless", + "label": "Idle state size scale", + "group": "Size", + "name": "IdleScale" + }, + { + "value": "1", + "type": "Number", + "unit": "Dimensionless", + "label": "Focused state size scale", + "description": "The state is Focused when the button is hovered or held outside.", + "group": "Size", + "name": "FocusedScale" + }, + { + "value": "0.1", + "type": "Number", + "unit": "Second", + "label": "Fade-in duration", + "group": "Speed", + "name": "FadeInDuration" + }, + { + "value": "0.2", + "type": "Number", + "unit": "Second", + "label": "Fade-out duration", + "group": "Speed", + "name": "FadeOutDuration" + }, + { + "value": "0.95", + "type": "Number", + "unit": "Dimensionless", + "label": "Pressed state size scale", + "group": "Size", + "name": "PressedScale" + }, + { + "value": "easeInOutQuad", + "type": "Choice", + "label": "Fade-in easing", + "group": "Speed", + "choices": [ + { + "label": "linear", + "value": "linear" + }, + { + "label": "easeInQuad", + "value": "easeInQuad" + }, + { + "label": "easeOutQuad", + "value": "easeOutQuad" + }, + { + "label": "easeInOutQuad", + "value": "easeInOutQuad" + }, + { + "label": "easeInCubic", + "value": "easeInCubic" + }, + { + "label": "easeOutCubic", + "value": "easeOutCubic" + }, + { + "label": "easeInOutCubic", + "value": "easeInOutCubic" + }, + { + "label": "easeInQuart", + "value": "easeInQuart" + }, + { + "label": "easeOutQuart", + "value": "easeOutQuart" + }, + { + "label": "easeInOutQuart", + "value": "easeInOutQuart" + }, + { + "label": "easeInQuint", + "value": "easeInQuint" + }, + { + "label": "easeOutQuint", + "value": "easeOutQuint" + }, + { + "label": "easeInOutQuint", + "value": "easeInOutQuint" + }, + { + "label": "easeInOutSine", + "value": "easeInOutSine" + }, + { + "label": "easeInExpo", + "value": "easeInExpo" + }, + { + "label": "easeOutExpo", + "value": "easeOutExpo" + }, + { + "label": "easeInOutExpo", + "value": "easeInOutExpo" + }, + { + "label": "easeInCirc", + "value": "easeInCirc" + }, + { + "label": "easeOutCirc", + "value": "easeOutCirc" + }, + { + "label": "easeInOutCirc", + "value": "easeInOutCirc" + }, + { + "label": "easeOutBounce", + "value": "easeOutBounce" + }, + { + "label": "easeInBack", + "value": "easeInBack" + }, + { + "label": "easeOutBack", + "value": "easeOutBack" + }, + { + "label": "easeInOutBack", + "value": "easeInOutBack" + }, + { + "label": "elastic", + "value": "elastic" + }, + { + "label": "swingFromTo", + "value": "swingFromTo" + }, + { + "label": "swingFrom", + "value": "swingFrom" + }, + { + "label": "swingTo", + "value": "swingTo" + }, + { + "label": "bounce", + "value": "bounce" + }, + { + "label": "bouncePast", + "value": "bouncePast" + }, + { + "label": "easeFromTo", + "value": "easeFromTo" + }, + { + "label": "easeFrom", + "value": "easeFrom" + }, + { + "label": "easeTo", + "value": "easeTo" + } + ], + "name": "FadeInEasing" + }, + { + "value": "easeInOutQuad", + "type": "Choice", + "label": "Fade-out easing", + "group": "Speed", + "choices": [ + { + "label": "linear", + "value": "linear" + }, + { + "label": "easeInQuad", + "value": "easeInQuad" + }, + { + "label": "easeOutQuad", + "value": "easeOutQuad" + }, + { + "label": "easeInOutQuad", + "value": "easeInOutQuad" + }, + { + "label": "easeInCubic", + "value": "easeInCubic" + }, + { + "label": "easeOutCubic", + "value": "easeOutCubic" + }, + { + "label": "easeInOutCubic", + "value": "easeInOutCubic" + }, + { + "label": "easeInQuart", + "value": "easeInQuart" + }, + { + "label": "easeOutQuart", + "value": "easeOutQuart" + }, + { + "label": "easeInOutQuart", + "value": "easeInOutQuart" + }, + { + "label": "easeInQuint", + "value": "easeInQuint" + }, + { + "label": "easeOutQuint", + "value": "easeOutQuint" + }, + { + "label": "easeInOutQuint", + "value": "easeInOutQuint" + }, + { + "label": "easeInOutSine", + "value": "easeInOutSine" + }, + { + "label": "easeInExpo", + "value": "easeInExpo" + }, + { + "label": "easeOutExpo", + "value": "easeOutExpo" + }, + { + "label": "easeInOutExpo", + "value": "easeInOutExpo" + }, + { + "label": "easeInCirc", + "value": "easeInCirc" + }, + { + "label": "easeOutCirc", + "value": "easeOutCirc" + }, + { + "label": "easeInOutCirc", + "value": "easeInOutCirc" + }, + { + "label": "easeOutBounce", + "value": "easeOutBounce" + }, + { + "label": "easeInBack", + "value": "easeInBack" + }, + { + "label": "easeOutBack", + "value": "easeOutBack" + }, + { + "label": "easeInOutBack", + "value": "easeInOutBack" + }, + { + "label": "elastic", + "value": "elastic" + }, + { + "label": "swingFromTo", + "value": "swingFromTo" + }, + { + "label": "swingFrom", + "value": "swingFrom" + }, + { + "label": "swingTo", + "value": "swingTo" + }, + { + "label": "bounce", + "value": "bounce" + }, + { + "label": "bouncePast", + "value": "bouncePast" + }, + { + "label": "easeFromTo", + "value": "easeFromTo" + }, + { + "label": "easeFrom", + "value": "easeFrom" + }, + { + "label": "easeTo", + "value": "easeTo" + } + ], + "name": "FadeOutEasing" + }, + { + "value": "Idle", + "type": "Choice", + "label": "", + "choices": [ + { + "label": "Idle", + "value": "Idle" + }, + { + "label": "Focused", + "value": "Focused" + }, + { + "label": "Pressed", + "value": "Pressed" + } + ], + "hidden": true, + "name": "PreviousState" + } + ], + "propertiesFolderStructure": { + "folderName": "__ROOT", + "children": [ + { + "propertyName": "Scale" + }, + { + "propertyName": "ButtonFSM" + }, + { + "propertyName": "Tween" + }, + { + "folderName": "Size", + "children": [ + { + "propertyName": "IdleScale" + }, + { + "propertyName": "FocusedScale" + }, + { + "propertyName": "PressedScale" + } + ] + }, + { + "folderName": "Speed", + "children": [ + { + "propertyName": "FadeInDuration" + }, + { + "propertyName": "FadeOutDuration" + }, + { + "propertyName": "FadeInEasing" + }, + { + "propertyName": "FadeOutEasing" + } + ] + }, + { + "propertyName": "PreviousState" + } + ] + } + }, + { + "description": "Smoothly change the color tint of buttons according to their state.", + "fullName": "Button color tint tween", + "helpPath": "", + "iconUrl": "", + "name": "ButtonColorTintTween", + "objectType": "Sprite", + "previewIconUrl": "", + "eventsFunctions": [ + { + "fullName": "", + "functionType": "Action", + "name": "onCreated", + "sentence": "", + "events": [ + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [], + "actions": [ + { + "type": { + "value": "ChangeColor" + }, + "parameters": [ + "Object", + "IdleColorTint" + ] + } + ] + } + ], + "parameters": [ + { + "description": "Object", + "name": "Object", + "supplementaryInformation": "Sprite", + "type": "object" + }, + { + "description": "Behavior", + "name": "Behavior", + "supplementaryInformation": "ButtonStates::ButtonColorTintTween", + "type": "behavior" + } + ], + "objectGroups": [] + }, + { + "fullName": "", + "functionType": "Action", + "name": "doStepPreEvents", + "sentence": "", + "events": [ + { + "colorB": 228, + "colorG": 176, + "colorR": 74, + "creationTime": 0, + "name": "States", + "source": "", + "type": "BuiltinCommonInstructions::Group", + "events": [ + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [ + { + "type": { + "value": "ButtonStates::ButtonFSM::IsIdle" + }, + "parameters": [ + "Object", + "ButtonFSM", + "" + ] + }, + { + "type": { + "value": "StringVariable" + }, + "parameters": [ + "PreviousState", + "!=", + "\"Idle\"" + ] + } + ], + "actions": [ + { + "type": { + "value": "SetStringVariable" + }, + "parameters": [ + "PreviousState", + "=", + "\"Idle\"" + ] + }, + { + "type": { + "value": "ButtonStates::ButtonColorTintTween::FadeOut" + }, + "parameters": [ + "Object", + "Behavior", + "IdleColorTint", + "" + ] + } + ] + }, + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [ + { + "type": { + "value": "ButtonStates::ButtonFSM::IsFocused" + }, + "parameters": [ + "Object", + "ButtonFSM", + "" + ] + } + ], + "actions": [], + "events": [ + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [ + { + "type": { + "value": "StringVariable" + }, + "parameters": [ + "PreviousState", + "=", + "\"Idle\"" + ] + } + ], + "actions": [ + { + "type": { + "value": "SetStringVariable" + }, + "parameters": [ + "PreviousState", + "=", + "\"Focused\"" + ] + }, + { + "type": { + "value": "ButtonStates::ButtonColorTintTween::FadeIn" + }, + "parameters": [ + "Object", + "Behavior", + "FocusedColorTint", + "" + ] + } + ] + }, + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [ + { + "type": { + "value": "StringVariable" + }, + "parameters": [ + "PreviousState", + "=", + "\"Pressed\"" + ] + } + ], + "actions": [ + { + "type": { + "value": "SetStringVariable" + }, + "parameters": [ + "PreviousState", + "=", + "\"Focused\"" + ] + }, + { + "type": { + "value": "ButtonStates::ButtonColorTintTween::FadeOut" + }, + "parameters": [ + "Object", + "Behavior", + "FocusedColorTint", + "" + ] + } + ] + } + ] + }, + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [ + { + "type": { + "value": "ButtonStates::ButtonFSM::IsPressed" + }, + "parameters": [ + "Object", + "ButtonFSM", + "" + ] + }, + { + "type": { + "value": "StringVariable" + }, + "parameters": [ + "PreviousState", + "!=", + "\"Pressed\"" + ] + } + ], + "actions": [ + { + "type": { + "value": "SetStringVariable" + }, + "parameters": [ + "PreviousState", + "=", + "\"Pressed\"" + ] + }, + { + "type": { + "value": "ButtonStates::ButtonColorTintTween::FadeIn" + }, + "parameters": [ + "Object", + "Behavior", + "PressedColorTint", + "" + ] + } + ] + } + ], + "parameters": [] + } + ], + "parameters": [ + { + "description": "Object", + "name": "Object", + "supplementaryInformation": "Sprite", + "type": "object" + }, + { + "description": "Behavior", + "name": "Behavior", + "supplementaryInformation": "ButtonStates::ButtonColorTintTween", + "type": "behavior" + } + ], + "objectGroups": [] + }, + { + "fullName": "Fade in", + "functionType": "Action", + "name": "FadeIn", + "private": true, + "sentence": "_PARAM0_ fade in to _PARAM2_", + "events": [ + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [], + "actions": [ + { + "type": { + "value": "Tween::AddObjectColorTween" + }, + "parameters": [ + "Object", + "Tween", + "\"__ButtonColorTintTween.Fade\"", + "Value", + "FadeInEasing", + "1000 * FadeInDuration", + "no", + "yes" + ] + } + ] + } + ], + "parameters": [ + { + "description": "Object", + "name": "Object", + "supplementaryInformation": "Sprite", + "type": "object" + }, + { + "description": "Behavior", + "name": "Behavior", + "supplementaryInformation": "ButtonStates::ButtonColorTintTween", + "type": "behavior" + }, + { + "description": "Value", + "name": "Value", + "type": "color" + } + ], + "objectGroups": [] + }, + { + "fullName": "Fade out", + "functionType": "Action", + "name": "FadeOut", + "private": true, + "sentence": "_PARAM0_ fade out to _PARAM2_", + "events": [ + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [], + "actions": [ + { + "type": { + "value": "Tween::AddObjectColorTween" + }, + "parameters": [ + "Object", + "Tween", + "\"__ButtonColorTintTween.Fade\"", + "Value", + "FadeOutEasing", + "1000 * FadeOutDuration", + "no", + "yes" + ] + } + ] + } + ], + "parameters": [ + { + "description": "Object", + "name": "Object", + "supplementaryInformation": "Sprite", + "type": "object" + }, + { + "description": "Behavior", + "name": "Behavior", + "supplementaryInformation": "ButtonStates::ButtonColorTintTween", + "type": "behavior" + }, + { + "description": "Value", + "name": "Value", + "type": "color" + } + ], + "objectGroups": [] + }, + { + "description": "the idle state color tint of the object.", + "fullName": "Idle state color tint", + "functionType": "ExpressionAndCondition", + "group": "Button color tint tween", + "name": "IdleColorTint", + "sentence": "the idle state color tint", + "events": [ + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [], + "actions": [ + { + "type": { + "value": "SetReturnString" + }, + "parameters": [ + "IdleColorTint" + ] + } + ] + } + ], + "expressionType": { + "type": "color" + }, + "parameters": [ + { + "description": "Object", + "name": "Object", + "supplementaryInformation": "Sprite", + "type": "object" + }, + { + "description": "Behavior", + "name": "Behavior", + "supplementaryInformation": "ButtonStates::ButtonColorTintTween", + "type": "behavior" + } + ], + "objectGroups": [] + }, + { + "fullName": "", + "functionType": "ActionWithOperator", + "getterName": "IdleColorTint", + "group": "Button color tint tween", + "name": "SetIdleColorTint", + "sentence": "", + "events": [ + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [], + "actions": [ + { + "type": { + "value": "SetStringVariable" + }, + "parameters": [ + "IdleColorTint", + "=", + "Value" + ] + } + ] + } + ], + "parameters": [ + { + "description": "Object", + "name": "Object", + "supplementaryInformation": "Sprite", + "type": "object" + }, + { + "description": "Behavior", + "name": "Behavior", + "supplementaryInformation": "ButtonStates::ButtonColorTintTween", + "type": "behavior" + } + ], + "objectGroups": [] + }, + { + "description": "the focused state color tint of the object. The state is Focused when the button is hovered or held outside.", + "fullName": "Focused state color tint", + "functionType": "ExpressionAndCondition", + "group": "Button color tint tween", + "name": "FocusedColorTint", + "sentence": "the focused state color tint", + "events": [ + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [], + "actions": [ + { + "type": { + "value": "SetReturnString" + }, + "parameters": [ + "FocusedColorTint" + ] + } + ] + } + ], + "expressionType": { + "type": "color" + }, + "parameters": [ + { + "description": "Object", + "name": "Object", + "supplementaryInformation": "Sprite", + "type": "object" + }, + { + "description": "Behavior", + "name": "Behavior", + "supplementaryInformation": "ButtonStates::ButtonColorTintTween", + "type": "behavior" + } + ], + "objectGroups": [] + }, + { + "fullName": "", + "functionType": "ActionWithOperator", + "getterName": "FocusedColorTint", + "group": "Button color tint tween", + "name": "SetFocusedColorTint", + "sentence": "", + "events": [ + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [], + "actions": [ + { + "type": { + "value": "SetStringVariable" + }, + "parameters": [ + "FocusedColorTint", + "=", + "Value" + ] + } + ] + } + ], + "parameters": [ + { + "description": "Object", + "name": "Object", + "supplementaryInformation": "Sprite", + "type": "object" + }, + { + "description": "Behavior", + "name": "Behavior", + "supplementaryInformation": "ButtonStates::ButtonColorTintTween", + "type": "behavior" + } + ], + "objectGroups": [] + }, + { + "description": "the pressed state color tint of the object.", + "fullName": "Pressed state color tint", + "functionType": "ExpressionAndCondition", + "group": "Button color tint tween", + "name": "PressedColorTint", + "sentence": "the pressed state color tint", + "events": [ + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [], + "actions": [ + { + "type": { + "value": "SetReturnString" + }, + "parameters": [ + "PressedColorTint" + ] + } + ] + } + ], + "expressionType": { + "type": "color" + }, + "parameters": [ + { + "description": "Object", + "name": "Object", + "supplementaryInformation": "Sprite", + "type": "object" + }, + { + "description": "Behavior", + "name": "Behavior", + "supplementaryInformation": "ButtonStates::ButtonColorTintTween", + "type": "behavior" + } + ], + "objectGroups": [] + }, + { + "fullName": "", + "functionType": "ActionWithOperator", + "getterName": "PressedColorTint", + "group": "Button color tint tween", + "name": "SetPressedColorTint", + "sentence": "", + "events": [ + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [], + "actions": [ + { + "type": { + "value": "SetStringVariable" + }, + "parameters": [ + "PressedColorTint", + "=", + "Value" + ] + } + ] + } + ], + "parameters": [ + { + "description": "Object", + "name": "Object", + "supplementaryInformation": "Sprite", + "type": "object" + }, + { + "description": "Behavior", + "name": "Behavior", + "supplementaryInformation": "ButtonStates::ButtonColorTintTween", + "type": "behavior" + } + ], + "objectGroups": [] + }, + { + "description": "the fade-in duration of the object.", + "fullName": "Fade-in duration", + "functionType": "ExpressionAndCondition", + "group": "Button color tint tween", + "name": "FadeInDuration", + "sentence": "the fade-in duration", + "events": [ + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [], + "actions": [ + { + "type": { + "value": "SetReturnNumber" + }, + "parameters": [ + "FadeInDuration" + ] + } + ] + } + ], + "expressionType": { + "type": "expression" + }, + "parameters": [ + { + "description": "Object", + "name": "Object", + "supplementaryInformation": "Sprite", + "type": "object" + }, + { + "description": "Behavior", + "name": "Behavior", + "supplementaryInformation": "ButtonStates::ButtonColorTintTween", + "type": "behavior" + } + ], + "objectGroups": [] + }, + { + "fullName": "", + "functionType": "ActionWithOperator", + "getterName": "FadeInDuration", + "group": "Button color tint tween", + "name": "SetFadeInDuration", + "sentence": "", + "events": [ + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [], + "actions": [ + { + "type": { + "value": "SetNumberVariable" + }, + "parameters": [ + "FadeInDuration", + "=", + "Value" + ] + } + ] + } + ], + "parameters": [ + { + "description": "Object", + "name": "Object", + "supplementaryInformation": "Sprite", + "type": "object" + }, + { + "description": "Behavior", + "name": "Behavior", + "supplementaryInformation": "ButtonStates::ButtonColorTintTween", + "type": "behavior" + } + ], + "objectGroups": [] + }, + { + "description": "the fade-out duration of the object.", + "fullName": "Fade-out duration", + "functionType": "ExpressionAndCondition", + "group": "Button color tint tween", + "name": "FadeOutDuration", + "sentence": "the fade-out duration", + "events": [ + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [], + "actions": [ + { + "type": { + "value": "SetReturnNumber" + }, + "parameters": [ + "FadeOutDuration" + ] + } + ] + } + ], + "expressionType": { + "type": "expression" + }, + "parameters": [ + { + "description": "Object", + "name": "Object", + "supplementaryInformation": "Sprite", + "type": "object" + }, + { + "description": "Behavior", + "name": "Behavior", + "supplementaryInformation": "ButtonStates::ButtonColorTintTween", + "type": "behavior" + } + ], + "objectGroups": [] + }, + { + "fullName": "", + "functionType": "ActionWithOperator", + "getterName": "FadeOutDuration", + "group": "Button color tint tween", + "name": "SetFadeOutDuration", + "sentence": "", + "events": [ + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [], + "actions": [ + { + "type": { + "value": "SetNumberVariable" + }, + "parameters": [ + "FadeOutDuration", + "=", + "Value" + ] + } + ] + } + ], + "parameters": [ + { + "description": "Object", + "name": "Object", + "supplementaryInformation": "Sprite", + "type": "object" + }, + { + "description": "Behavior", + "name": "Behavior", + "supplementaryInformation": "ButtonStates::ButtonColorTintTween", + "type": "behavior" + } + ], + "objectGroups": [] + }, + { + "description": "the fade-in easing of the object.", + "fullName": "Fade-in easing", + "functionType": "ExpressionAndCondition", + "group": "Button color tint tween", + "name": "FadeInEasing", + "sentence": "the fade-in easing", + "events": [ + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [], + "actions": [ + { + "type": { + "value": "SetReturnString" + }, + "parameters": [ + "FadeInEasing" + ] + } + ] + } + ], + "expressionType": { + "supplementaryInformation": "[\"linear\",\"easeInQuad\",\"easeOutQuad\",\"easeInOutQuad\",\"easeInCubic\",\"easeOutCubic\",\"easeInOutCubic\",\"easeInQuart\",\"easeOutQuart\",\"easeInOutQuart\",\"easeInQuint\",\"easeOutQuint\",\"easeInOutQuint\",\"easeInOutSine\",\"easeInExpo\",\"easeOutExpo\",\"easeInOutExpo\",\"easeInCirc\",\"easeOutCirc\",\"easeInOutCirc\",\"easeOutBounce\",\"easeInBack\",\"easeOutBack\",\"easeInOutBack\",\"elastic\",\"swingFromTo\",\"swingFrom\",\"swingTo\",\"bounce\",\"bouncePast\",\"easeFromTo\",\"easeFrom\",\"easeTo\"]", + "type": "stringWithSelector" + }, + "parameters": [ + { + "description": "Object", + "name": "Object", + "supplementaryInformation": "Sprite", + "type": "object" + }, + { + "description": "Behavior", + "name": "Behavior", + "supplementaryInformation": "ButtonStates::ButtonColorTintTween", + "type": "behavior" + } + ], + "objectGroups": [] + }, + { + "fullName": "", + "functionType": "ActionWithOperator", + "getterName": "FadeInEasing", + "group": "Button color tint tween", + "name": "SetFadeInEasing", + "sentence": "", + "events": [ + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [], + "actions": [ + { + "type": { + "value": "SetStringVariable" + }, + "parameters": [ + "FadeInEasing", + "=", + "Value" + ] + } + ] + } + ], + "parameters": [ + { + "description": "Object", + "name": "Object", + "supplementaryInformation": "Sprite", + "type": "object" + }, + { + "description": "Behavior", + "name": "Behavior", + "supplementaryInformation": "ButtonStates::ButtonColorTintTween", + "type": "behavior" + } + ], + "objectGroups": [] + }, + { + "description": "the fade-out easing of the object.", + "fullName": "Fade-out easing", + "functionType": "ExpressionAndCondition", + "group": "Button color tint tween", + "name": "FadeOutEasing", + "sentence": "the fade-out easing", + "events": [ + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [], + "actions": [ + { + "type": { + "value": "SetReturnString" + }, + "parameters": [ + "FadeOutEasing" + ] + } + ] + } + ], + "expressionType": { + "supplementaryInformation": "[\"linear\",\"easeInQuad\",\"easeOutQuad\",\"easeInOutQuad\",\"easeInCubic\",\"easeOutCubic\",\"easeInOutCubic\",\"easeInQuart\",\"easeOutQuart\",\"easeInOutQuart\",\"easeInQuint\",\"easeOutQuint\",\"easeInOutQuint\",\"easeInOutSine\",\"easeInExpo\",\"easeOutExpo\",\"easeInOutExpo\",\"easeInCirc\",\"easeOutCirc\",\"easeInOutCirc\",\"easeOutBounce\",\"easeInBack\",\"easeOutBack\",\"easeInOutBack\",\"elastic\",\"swingFromTo\",\"swingFrom\",\"swingTo\",\"bounce\",\"bouncePast\",\"easeFromTo\",\"easeFrom\",\"easeTo\"]", + "type": "stringWithSelector" + }, + "parameters": [ + { + "description": "Object", + "name": "Object", + "supplementaryInformation": "Sprite", + "type": "object" + }, + { + "description": "Behavior", + "name": "Behavior", + "supplementaryInformation": "ButtonStates::ButtonColorTintTween", + "type": "behavior" + } + ], + "objectGroups": [] + }, + { + "fullName": "", + "functionType": "ActionWithOperator", + "getterName": "FadeOutEasing", + "group": "Button color tint tween", + "name": "SetFadeOutEasing", + "sentence": "", + "events": [ + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [], + "actions": [ + { + "type": { + "value": "SetStringVariable" + }, + "parameters": [ + "FadeOutEasing", + "=", + "Value" + ] + } + ] + } + ], + "parameters": [ + { + "description": "Object", + "name": "Object", + "supplementaryInformation": "Sprite", + "type": "object" + }, + { + "description": "Behavior", + "name": "Behavior", + "supplementaryInformation": "ButtonStates::ButtonColorTintTween", + "type": "behavior" + } + ], + "objectGroups": [] + } + ], + "eventsFunctionsFolderStructure": { + "folderName": "__ROOT", + "children": [ + { + "functionName": "onCreated" + }, + { + "functionName": "doStepPreEvents" + }, + { + "functionName": "FadeIn" + }, + { + "functionName": "FadeOut" + }, + { + "folderName": "Button color tint tween", + "children": [ + { + "functionName": "IdleColorTint" + }, + { + "functionName": "SetIdleColorTint" + }, + { + "functionName": "FocusedColorTint" + }, + { + "functionName": "SetFocusedColorTint" + }, + { + "functionName": "PressedColorTint" + }, + { + "functionName": "SetPressedColorTint" + }, + { + "functionName": "FadeInDuration" + }, + { + "functionName": "SetFadeInDuration" + }, + { + "functionName": "FadeOutDuration" + }, + { + "functionName": "SetFadeOutDuration" + }, + { + "functionName": "FadeInEasing" + }, + { + "functionName": "SetFadeInEasing" + }, + { + "functionName": "FadeOutEasing" + }, + { + "functionName": "SetFadeOutEasing" + } + ] + } + ] + }, + "propertyDescriptors": [ + { + "value": "", + "type": "Behavior", + "label": "Button states", + "extraInformation": [ + "ButtonStates::ButtonFSM" + ], + "choices": [], + "name": "ButtonFSM" + }, + { + "value": "", + "type": "Behavior", + "label": "Tween", + "extraInformation": [ + "Tween::TweenBehavior" + ], + "choices": [], + "name": "Tween" + }, + { + "value": "255;255;255", + "type": "Color", + "label": "Idle state color tint", + "group": "Color", + "name": "IdleColorTint" + }, + { + "value": "192;192;192", + "type": "Color", + "label": "Focused state color tint", + "description": "The state is Focused when the button is hovered or held outside.", + "group": "Color", + "name": "FocusedColorTint" + }, + { + "value": "64;64;64", + "type": "Color", + "label": "Pressed state color tint", + "group": "Color", + "name": "PressedColorTint" + }, + { + "value": "0.1", + "type": "Number", + "unit": "Second", + "label": "Fade-in duration", + "group": "Speed", + "name": "FadeInDuration" + }, + { + "value": "0.2", + "type": "Number", + "unit": "Second", + "label": "Fade-out duration", + "group": "Speed", + "name": "FadeOutDuration" + }, + { + "value": "easeInOutQuad", + "type": "Choice", + "label": "Fade-in easing", + "group": "Speed", + "choices": [ + { + "label": "linear", + "value": "linear" + }, + { + "label": "easeInQuad", + "value": "easeInQuad" + }, + { + "label": "easeOutQuad", + "value": "easeOutQuad" + }, + { + "label": "easeInOutQuad", + "value": "easeInOutQuad" + }, + { + "label": "easeInCubic", + "value": "easeInCubic" + }, + { + "label": "easeOutCubic", + "value": "easeOutCubic" + }, + { + "label": "easeInOutCubic", + "value": "easeInOutCubic" + }, + { + "label": "easeInQuart", + "value": "easeInQuart" + }, + { + "label": "easeOutQuart", + "value": "easeOutQuart" + }, + { + "label": "easeInOutQuart", + "value": "easeInOutQuart" + }, + { + "label": "easeInQuint", + "value": "easeInQuint" + }, + { + "label": "easeOutQuint", + "value": "easeOutQuint" + }, + { + "label": "easeInOutQuint", + "value": "easeInOutQuint" + }, + { + "label": "easeInOutSine", + "value": "easeInOutSine" + }, + { + "label": "easeInExpo", + "value": "easeInExpo" + }, + { + "label": "easeOutExpo", + "value": "easeOutExpo" + }, + { + "label": "easeInOutExpo", + "value": "easeInOutExpo" + }, + { + "label": "easeInCirc", + "value": "easeInCirc" + }, + { + "label": "easeOutCirc", + "value": "easeOutCirc" + }, + { + "label": "easeInOutCirc", + "value": "easeInOutCirc" + }, + { + "label": "easeOutBounce", + "value": "easeOutBounce" + }, + { + "label": "easeInBack", + "value": "easeInBack" + }, + { + "label": "easeOutBack", + "value": "easeOutBack" + }, + { + "label": "easeInOutBack", + "value": "easeInOutBack" + }, + { + "label": "elastic", + "value": "elastic" + }, + { + "label": "swingFromTo", + "value": "swingFromTo" + }, + { + "label": "swingFrom", + "value": "swingFrom" + }, + { + "label": "swingTo", + "value": "swingTo" + }, + { + "label": "bounce", + "value": "bounce" + }, + { + "label": "bouncePast", + "value": "bouncePast" + }, + { + "label": "easeFromTo", + "value": "easeFromTo" + }, + { + "label": "easeFrom", + "value": "easeFrom" + }, + { + "label": "easeTo", + "value": "easeTo" + } + ], + "name": "FadeInEasing" + }, + { + "value": "easeInOutQuad", + "type": "Choice", + "label": "Fade-out easing", + "group": "Speed", + "choices": [ + { + "label": "linear", + "value": "linear" + }, + { + "label": "easeInQuad", + "value": "easeInQuad" + }, + { + "label": "easeOutQuad", + "value": "easeOutQuad" + }, + { + "label": "easeInOutQuad", + "value": "easeInOutQuad" + }, + { + "label": "easeInCubic", + "value": "easeInCubic" + }, + { + "label": "easeOutCubic", + "value": "easeOutCubic" + }, + { + "label": "easeInOutCubic", + "value": "easeInOutCubic" + }, + { + "label": "easeInQuart", + "value": "easeInQuart" + }, + { + "label": "easeOutQuart", + "value": "easeOutQuart" + }, + { + "label": "easeInOutQuart", + "value": "easeInOutQuart" + }, + { + "label": "easeInQuint", + "value": "easeInQuint" + }, + { + "label": "easeOutQuint", + "value": "easeOutQuint" + }, + { + "label": "easeInOutQuint", + "value": "easeInOutQuint" + }, + { + "label": "easeInOutSine", + "value": "easeInOutSine" + }, + { + "label": "easeInExpo", + "value": "easeInExpo" + }, + { + "label": "easeOutExpo", + "value": "easeOutExpo" + }, + { + "label": "easeInOutExpo", + "value": "easeInOutExpo" + }, + { + "label": "easeInCirc", + "value": "easeInCirc" + }, + { + "label": "easeOutCirc", + "value": "easeOutCirc" + }, + { + "label": "easeInOutCirc", + "value": "easeInOutCirc" + }, + { + "label": "easeOutBounce", + "value": "easeOutBounce" + }, + { + "label": "easeInBack", + "value": "easeInBack" + }, + { + "label": "easeOutBack", + "value": "easeOutBack" + }, + { + "label": "easeInOutBack", + "value": "easeInOutBack" + }, + { + "label": "elastic", + "value": "elastic" + }, + { + "label": "swingFromTo", + "value": "swingFromTo" + }, + { + "label": "swingFrom", + "value": "swingFrom" + }, + { + "label": "swingTo", + "value": "swingTo" + }, + { + "label": "bounce", + "value": "bounce" + }, + { + "label": "bouncePast", + "value": "bouncePast" + }, + { + "label": "easeFromTo", + "value": "easeFromTo" + }, + { + "label": "easeFrom", + "value": "easeFrom" + }, + { + "label": "easeTo", + "value": "easeTo" + } + ], + "name": "FadeOutEasing" + }, + { + "value": "Idle", + "type": "Choice", + "label": "", + "choices": [ + { + "label": "Idle", + "value": "Idle" + }, + { + "label": "Focused", + "value": "Focused" + }, + { + "label": "Pressed", + "value": "Pressed" + } + ], + "hidden": true, + "name": "PreviousState" + } + ], + "propertiesFolderStructure": { + "folderName": "__ROOT", + "children": [ + { + "propertyName": "ButtonFSM" + }, + { + "propertyName": "Tween" + }, + { + "folderName": "Color", + "children": [ + { + "propertyName": "IdleColorTint" + }, + { + "propertyName": "FocusedColorTint" + }, + { + "propertyName": "PressedColorTint" + } + ] + }, + { + "folderName": "Speed", + "children": [ + { + "propertyName": "FadeInDuration" + }, + { + "propertyName": "FadeOutDuration" + }, + { + "propertyName": "FadeInEasing" + }, + { + "propertyName": "FadeOutEasing" + } + ] + }, + { + "propertyName": "PreviousState" + } + ] + } + } + ], + "eventsBasedObjects": [] } ], "externalLayouts": [] diff --git a/examples/starting-3d-car-racing/assets/Flat dark joystick border horizontal.png b/examples/starting-3d-car-racing/assets/Flat dark joystick border horizontal.png new file mode 100644 index 0000000000000000000000000000000000000000..4f772d873797453c44bb85fc6df481926391d4fa GIT binary patch literal 4013 zcmV;e4^r@nP)005u}1^@s6i_d2*00006VoOIv0RI60 z0RN!9r;`8x010qNS#tmY3labT3lag+-G2N4000McNliru>IW4L8Up%JUjzUE4?0Oi zK~#9!?VW9GTi1EVe-BC76e-2B!<)#C1F*G<(y13KfLpljJ_tyHDOiHdLxTorhPkMN zET5VH87q(cxagN+93zfahyI?JuD)Jfdjx- zfJ_apPRawazz4v)z&P-(>$(LCV8DR`Mu3NaZvw+zfUOxQCxKr9W58HgfDAN%sZ?sO zhY}xQ$c--T~eO-f~?xX8|__C@#=QnRCK7;R3;3cYy0DP-xG)jMe|IOc3l|rFFxm@-;ggM~5 zuIr9j0B_Ir;v#aqR^9qLX%^v*JMKW_cGIaUpMU;2s%k=h=o@b+K7cPHB5zP#iQ;?p z_xBTxMpyk`xm;#`e%_QKWfFMYb=~Y%UPNSzL++Cz@}4R1B@&4>;kyxzmlYNOq;=hZf{GVzH|iE?tH%4g3-V7Y>NXy5Vl}Eg!Tew9LE_6 z1c0ejY7{ud%1@D5`B#0wsaoHe3*Z;`9~`ZX@!1tD<|!^BZ+vn8!Hz=Mu^e#U{(~b@ z^?dQq@*msoG*CNIdjLwRJTjdZw_8}3up_gH6~&w4`R;H%dZTK?)(cCu|N zt=r}S)0yFTMfG(y7-Y7d&{+5YhLGs%>GW{ScL2sj|5jkMO%1j3AAC3TP^?$~v0dkF z%Utl#{)0~fKWM7PPxN!WIB$F2EckBdYdf96>8trG=e9ft+;`vo8BuwU7RUN*PG}r_ zH&jCPfvN1dO|2)~R0vLIhU230I-3}1w&B=F_yFRFyphff$F~f?nCNlZ@ToNmp>got zP$nju%LiKy!L^x=|7xdEY&bRwzME3j$ELIA-s&2F>CA9ECVGMD_O)0D!wX+cGidqu zS{WZ~DFoL-Z`vROS#{R7dy(i5S~;+354bOL@URGHI&CcL3C+8)YxBiT?ZD8K2TW&% z<08Bi6oXm#+AD!qnw=$S3c#4?QL3AJV45ImF_9-vxy40hXyHXIvu z1J9x5FEm<^H0}XKU)T!xtS2ZTx?t+hR!cXu}~`gzwmnvTbU!zTHp=9?A@-Yh!=LD=6VOFgH}C za_`g!=V#XUfTE9DuVLHJ5k(^3Y9s{zu?=1J+B>8N*A2n7-K|^#xTC3Q;W(hhlqcTO z&Wjh{FRbbTwW1>gbUR&>IyAtQd`x3KBWs6XVT1t7gpLitfciu0<$wGb zd~?+tu$KQ_#gJ#W1uZ8G(NyAXd0?b2|2q^G`25!4AzMrlBieI#We64y6EgXs(y&v$^o~sFtHyc z!1Jz2Z!S0CMF4u@422!OZh>Lps}>?X+r9M75Uip@)+U_?#KKn}g`=7J958Jy(pgX} zd@Cy9bUlFfLDC%r#=^H+C0u3B0@z`2EPQK8Z}WhI)*$T%$ila-4@}(xP}#BBx4UCt zCwC3rLnIQh5C#;!NF;(YxQ87(ceUf~?z{z{kZPG@5mI*zGO+Vb;=R3W+aAc}giQez zRL{3gn0W?mhRiJ)j=16~Mxl^KFmE*}dmpdbVwAad(ju2X{)p?PQt06eD}>jtm$>}#M=UKawsLr>9>9D{ zuD4v~li5ow6y`$@U)UgASSawxd@_5fZD!qc2)1F@ zhU3hkcDt9$;?dv6=tkKItPx0a5et81)!+)fSm@$LbyBvSDC83xm-XJ_})s@|H@`TL9*A6CO&|!WX=A`Bd3lZo;dtfZ%whf(7MfI%> z0i60^Vg}3~lnzqI1*9$G|C>FR+t>qE_3yUBY@6DwpRD%B)d5s_)zoS@$io;0x2>e* z%1;{xa4I`72mIW2kZog|bb9)?=ktwwKmg^W?HJqE_DD~x^`A8X^h$CXn6e#W+q_BT zrY@X2-86u;RnjMHN7%M+Qa@VnpBrq?CMSV8+W{8B7Bz>KUu_w{S}}6M8s0+KQsxJ* zO=TzNHsJw5RgP2JO3NDDLfC{#XywI?UbG>A)7gnx^^q5}@R?y7g%i`+^Rt@q%F$?^?HLQiOo!+l;4_Nhr$G55?w(uEcvsuyd3yr?G;pZ&< zEuv_?yv*CnSUC2U5 z0LiFg9 z3uphO)q^+n%vDe2S=ju>mKHu^J>g`lp|>pn=CTtdRXM^Yrs*tv#zI&^^zl|hZ(9JY zR>c!prM00M3!k|VDm*py{<%#@zi2xeaW#MC+%9L(0lsG83q?-&!|4lW|Dhe{jiOSFHRKNb-qYD zFMcXJF^9;*tT9M!;WOG3&LH}5JK<})2fU$unc)K>dIspT@CCvX78Jfdec^1|7otQu zaq-jHiAhx+qc%Ka;WOG3mQ;DH6YzBmz$%1i$0~5-v;4;;+nvTjSgOF0>Fn9DPMt$K z-H22wH7X)+0CC%;MyaH#NAmgn=}w+Ry3rEHaYlgutj`Ipe60fTnCrS@oj#Ftt3{6E z8~{!Mdu{*vS~I{SuIqMX<-2sVWsc*dfnT!DIH6x9B1EH&O&pcWWmMHf_@;n|UDwTZ z&92>Uq2oAx!0W)MNj=lo*GDWCYv{F}=TRsWOoQ)j;BnV=yZXt;Xt!HgEEX3NiNt@1 zNH6dWQ+g*7i8Ku0;^HEnXVeC>|E#Kin$PEpU5_kV*=omeo&;Vor9w_7ll1iTYzSYm zSTqH^67ZDky8pIism)efz;T>REhHZ>p~w3B``63`%jGij^Yf;_mj#ZvuAAM;i->Hs z6id-VE@J>$-3G#+H7vPO(_bB@&4fBI3}d4?A^2s!F9&VcWKC04yvlm{4^5 zxE6BH`RSs>uhw!L=P>XRFznZB?CI%2Rs9a%WUbKf<V!?Q>AA(AIm^>jX;_e^h)1g(c0$4LQC0N())2c%i!RG;De zs>$9i6aX%RF->((>pud1-yqNE0lxs=R@I;7^ZA*e_EDe#Tn1wwFjD(^km}-xn2&&) z0Det%UEY}Mx`i005u}1^@s6i_d2*00006VoOIv0RI60 z0RN!9r;`8x010qNS#tmY3labT3lag+-G2N4000McNliru>IW4L83Z^Vsqz2-4)sYy zK~#9!?VW#U9Os$GKhN$;UTIg3o6I`06>?9My$ej#LSYc=z}?`OWx^StxUJ78qgMv3WyPu15qlIqyu^&!c8 zN$3;U2dWRm0`3E{fCZ%d&*fSKT>t+L@G+=)kU4}o6tkjA(<^RrdUkfwGrb*i01q5E zm?haUS`tPDH3af`An(H!q&%PiybHVqYC=5kt@qx$v}gc~IB+V}aRhiAcoZ0E0c^EE zIR*S07#DA4JT5>+8o+@EAKFJE@fF}P;An_|t0WhA1NeDKsn>sBxHxM7)CIcy&@Lo- z0{9v*8j)sA06$i%rJv0F?j6Sf7{cNq)uoOC-vo|n@TkD|fuXRTW4fyw1l(dV%pguGcwy%9g$V;-_2KrPfM>!ys$H23x!rpJu&;l> za&al_j`;7MlD`a_8zw^Kdig`gkmNgIhp(fv6DxQB?LXg_yPuBE&aeacd`i^i{=wlR z#sTll_3|U>l&J3k&xd^!6Uiid_I{RRYU5n@s^{^+wf8BluIi80!zzj@Q;4Kh%fFuh4MeKq8T-8p2yQT}q`A8fqF*PfTBG-TK3p0oXq{JS1=y z*oRS=HK*XonadZZA`^o9^TS63-Zb!Op?v~x_UBtOUl=%mpBo%LD#BUreu~T}yvmAj z_H%>7N3{mfvaF*E&b2d`6)ZX`z2ICsV_8<95C)b5=5o0stmS-ZfP1XHW=J*C$Ii9& zk1p_}ZQJA80GP|=hFHt_HiTURp?2`CYY}+Fw(X`DCW!#x>+_v2TSdc+!Ve$*Sf2IA zHoOj)%jL4b+fZlNrmeJYngd#vl?Gm;ib-bUgj&LfKen$~mX!(} zfFkk_tnEI~%my2U2flSZE+XG+*0Gzi7$KL-Jq>)f*6-acE|Bi-F$CxX-@3k#$z*1V z#p1=b=YY9fF7M}kbHIbbfA8)fZ2Z{RXcsNDtqh;7>wfdS};-Zxp> zUl{|Tf$t7YDWx8E9H;7sn`#QdB66aY@R@L|UEd9fNKLz1RPBJ4W$p3{kKT54rg1`T zF5DZy{%Q-7svS^7UTPP7#tF6Rt6jj0O?ALrF4qfOAp}e7jT35dp}I<`{f^^Q+^Mx% z2Rungp6f79s72qTMdV~t9577oXPi*03$rR0ti}OP62hn2j1z{wFVZ4XF&C67D&H%F zcz@=&J7LH##EKPhqC(|+f{UEqtG6KdAh0TDS`PY8a_42)@Glk`kYA=t7kOGMsB(>-0H8jdx-z&s$gSuv8> zqWJha8gD2RMJLp}O)20+Eg|?DX248anyjx?6@vZ!KdwY&@N^HyQM!0;Cv(mL>G5qtu)n*NO+H9csj`gK4aVV;#LlLga~h^ ztHZ!Xd@eKPGsboRMp|JW;F#Rad3-wnAB#f|gmIu_bSv@EO>;m$|N96{-rW|ZoG@l9 zdN$;N=lpFz^Krh=K3ZLPSLR%0uvg z>62hVG4S1`d^v!9rayuM#=y6!HaMVpk#s=d82Gl-0|vl=0W$DytNrBw_L}}^E=UHx zin7W9Yv zRs&X)oiCY8l1imWr_*$IcQ^X$vgH6~>%Ag+dV1*S=rG|pW?{Fpvy+~lo<<_xh7g>t z=W5uwb7!O-uNKhanDaHrK&oyB?C9u-biP+BgmEr78i<0~+Z5J>Z7M}3lQA`%sdVqE z0~Qw-@jS1l?h7#y{bVe#nRFeD=RB?1GrermsVC* zSXfvw#~>Ga+8B-(Fo^<&#f7VRdyi*6_s{!Uh>|%K>!irGH^z zfm^q3#TvdiLfC{TxWNG%O6rQmBChM=d0wO)u^Pal4B_hPDxT-zx^6S-00%yuhH)DG zZLaIa*7-L3*(grYtmk`8(#H*lU=w!Z>4Z(vd^v!#razhwl7VnryfQAZJ_BLEn%eF?A-^&3nR5zQzb^m7odBLU@R@T9SmWCPI3DFZ zP=^7IIE3!D0Q#}{mj*s_t^w(d^89Zz2aIGG)^R{13gOT1{ENE)R7(BKz-P`qAiZ7@ zK>v-_69zuhh9+ymw(Z%90Q&l=8939HChNzW{pD5wp4Pb45%<)iDE~X>)^mb-RD8v_0D?;D#9p)Al-b!!Ky-1JEx<#>@a12s3L&2ZO@0z1r2;!^vRgd&bF#*Q{P-J*9%;sj@LOD_+nWdyTJZx z0IX_{X#W>|$G~Ttu$k^xwFCMtxXX9JT?RhmgxhEy7_3x$tgGr*+!TvNH-7? zgU1q%s0l#-?#QzSJ_F$v zdckoVH!uMGf@A{tfq~CJxJltr^Fro&U)A+(0^C*Az^7fG&MKu|tmlh$0q9paFBtd? zguec~*3esX6}(AR{JTshvj;eA;EQ#2JZ9VWztsEXhTfLx+itzq{+bDbu<$!nOgBLTI zOb+;hfiD(0;Xm89{oQ69ziDqw_j7^s2ENEU;W?$$-!|*m(#(eV3l{#D3J)?F__WOl zr~SOIX>Tk{G_wKz{NNL8Gf8dW)7A;+fG3&>U(+41tTG-FkvD;?fiDtHxClIA+ji3z zq9lT7fa5q*z*CexOJd;D)(KtUsUYAB3_w2|k1M5)1FnHjTL@jH)N$Lk$AdafBFIKK zj&rWZ+IyT3K2>K_QErd5_qgLY=Yl+lw4x>bgTqGz{_D=1&?vN8REno&3KzzMK9IDk zMg0d44T+Moz&Q`v8;n9X%C6S>)0ZY^TXjHbbqfz19LbiHdQIS{P9w9s_W{yfT~+<;@^TS-{+e#^okz)6 zrwfxU-3ut$>Q?@rJ%8&SYtMfsgf8HZbs3#xDn+`hyQ-fpE-i4=UDgS{fAyB-Pk%o> zS#0%zrJb!F7#w~Y_&y=naiJPnyL;)*Y+pw$EiUk(eO;SMSHZJ0moNNByG}sb-2?px z59LM4Yrs(0M@vN5z2^aTY>^AzbY1>1e-)*a4)7I}lH)Ul$wE6%BH8X<_{hHQ?6mg$ zFd^h<;`08Wv|L;wlj$Lm*f@o{>UmtBzlzeM_!#q)9KTYSoNxO@9j0FK=L>%b^o4o! zIyySJfA7BA5Gqw-{^}enE4RW9-kegrFjKhjdT4U%F!jzy&h^=y)}FDjyJ4xc%F4tqE&y_2-92Mff*hB&_Gy)!mI!;4T-#4BnJdJUdgk z5X!e-Ntk=CFF%q_i8=xN9ngzWExG~xjkhePW@jfu`_duNp(h9OBfAjw67U?FOxd(i zF7O?cywd7P<;ViKt^@gzUM1?J0daHi6pW$d<(Uvvw~+*J6A=H5Hf@+0vQRswl)R!7 zcv=Hk9-5V?69Ruh$fs@s&@_rMkW)H@+DHPp5ys)82+sq565;Ud0lz?T%5&vhn2U?p z0l2RI!4V6kzAnNyfYFFFYXbNwN>1zScCi5Pju734sAIsFfJZfQMi2NUaGsL005u}0{{R3yb+fl0000RP)t-sOlfg7 zH8nOiHZ?UhHa0dbEiE=SHaIvqK|w)Bp7S06000McQchC%TOgH`2j0~ zbd=&pEnm-Qn*Ed|LF0YODA(b6AIsMyOb_)gR8czOT@Z_wyt@_S%GcS?SIE~>#B1d1 z7Ks*IH)@UjzYcb9tx>{rZjQRQ);M80K^R>+YCS@DJe$iwz9^x~kn~vbg$Z>?^*aIR z1yr3o5f0`Hd%5^<6F6UpP~Fjl;*KVq#|bI9VKHMl8{r`)iPZ%{-xG$jf4=?q~^F0yatw zOu%OeGYv3BH(H{F(ThOO^_a5`#3QH3){o1U6)b;h3(udjRn< zI5mr#b>?|3X4S#gW??4azTM2crLMJ``Q7sW_kj4vPGbWa|J4WksVTj~>o@!hO#Cb0 zh5iy9{~3b+AqW2{4F55aTL}yOgSNg)O8~XdByQ{5V_S;BJ4V)pUJQ3{4)9rx)hITN- z$pV}Y5IJBxyId#q5%+zE+-EK*`^E$)HbJH*ieN{jL`S++;dLTW1C(h6oP=CL1>1?+{;EmT?0T5AdBGkQj7xl&{aQ(}c)V~ z^X*e%3^Jziw;PzI-Q)Pxrd3xxEEP)IW485iPkW{Hp)}3kXR> zK~#9!?Oj=H8`l**_stBok)rm+alOTHY)jTcOO_Pdau+1&5)^3yq%HEtw|uhZ1*opyTSlNpA;d31v<-mnz&SAh32>in$=8J7LtSkrZm$^ucGWa^ z6!RueX69Q&R0kk4#epydaJxA@{SzY5+uL>0ye|c)so$qD^Rt?!y#%6%(#ihjpa33} zwtSU{|Jv1dLf$t5RM$Q7jHcj&qo=AHvH zcS_s(QP1g?WA~H*Teejd`1Ae0AQI2xJ^(YnIXQgmg^QO4CevPk+J^mmiNyN=@{&o4 zVR(>{nF#=xot=SYCOvlsnbnh>CyyRZO986tn{)y3TcPRC1JIJzWrHBEy*uVBgT{=5QcnwD$<2%zoR zxp}=+b&ZuN6`;C)U$gGXI0B%=iDW1=4MHq^%0dWen(kC?F4y&pqt$i$8dD-bZNvU= zX`1#vfc&(oSinvmE>F|7LxINTCma78=>)(5BZ0rEo-~mj}0bVfbBa1dD(v70D$6j@OmKx zgrIsyW}V4A}ur1`-h5}JruV9n#c1v5q;6+E@4)*S1PWL z%YrIv8+DJz(+Q$Vm%0RkxzDn!n%<5UJ8BOQLNtJ=!euPs-C!qx#;6vwqavW`y8af@ zZ_dtrA%SifuWhdgXiEy9XJl*u(H57hq~(G@me*Unv;ew(5P-*}Drqp7Xs{p(%Pf0~&CK7p|zqdWKpaih*^GR zUH~Dque+Q$3qjg9?+}0*TuL170z&KoAZS;hvEJ}xoN_5~9;RsqgsvOz&_k*&i**6BnP*W7-?<3WC5F?_yU*p`Lh_$VeO zLg4h8mF9dkLj-?b0rCn8p=lZ-;Yo}R55cmmc)nXgwGGX;i74unL$5CvW#tcmKDRi+ zG)-I`{20t?&62-i^Hz8~3rFx-Dcrhw9pTA|SQa!PD8_0=B0}jRvUAq%?SpLZ((6-%5XkYyTE9X@%+XK7 z6a5C6t{cf-8k(jB48t&tRdF{z6cG)v7C>8;1){Y#jz>hWZEIEBeJTKR)L)5&C!@~4 zkAx?&RuPFrmfWV4!sJ9KRzI=?12fTfky2vl=JiEIFwI#E58Yg|0^GiJ1Lh)8u9U*i z&1*5OnHfPHzz|}rb_`EWU}k0-Ma8Aa@%kX8L~v{rq2Sn>^#41{&8wF`MsBVjdHIFV z4Fi*t6Br*Ij`!IWA)T3TQykCnu`LU?Zd{wY+H>#S+$lmRIF3+oeA(PE^N;}Zjb)}E zm-R$(!+b>$QJYI#%1Wt0!N4__iIm}tur2eP%R_2lTh=84fI#B`cZ@j&`Maa_u$Y^c zqTD>-FqBfK=7%Dblpnd2I4;0D^8%12K7f0x%TfCwK_}(~7#|z!0bshkI48h#DBL?Q zz}1WWQ%b4BE-%hNO8LRS`L2n10RTV{{miu?XQE#S@$-4!Kl@`U93BKPOm0sw$is)d=)x_l)%__?2zYWe)< zcL{K|`;BCp8ZFJ5c^$f{XzIlF}*ihfT{o(A{=yRkKM}UB8{_Xzd9F$F@0f z4?#+aU~qiN?ZNReNCyO9=8nmU;6rCRT6{tZx4-!*1W$G`LoZ*uIxw90 z!Xk;N3_Q}H>)M+_h^l*XOh>6MY1=>S?YPI@DSy`x#_pyc=(_O&-6KzCXXZ zv-R)EFRdNIuDT|TB|m3)JU=C(O-R4xmdwl_o9666A+$gBo<8A7J?l6L+be1_gl??& zWMuqhu42kcCFuZWm{li0Pfmp=C(ieE&N_JlDUHI)+NLt0(OymHuaKY}b7e6(K)Ph+ z3(~e*zRiP*%l>rgtnHC$4;H4S$509AMTK!o)q_;4Vv8 zq-%bJcA5O|JulJAeuQX3P>IsnfaZ6Kar2%-@G*)QG=HZE&goRnsb>)#yPBG1sB!Hh z43d+OIs3N_GYkgUzhtn9^S>bspW_gq{VNwViuO^0PYl=T0VPPUzF*M(>6^wL0BHZ@ zvWDhTyoJGYF@JnE6Uejc!3TG#~e;_b+x__nEUko!5E+){gO+8MW_ zw!W>|D~;a);deGMLT(Z&UC$fvvaWY6#qkPCAl3O6NYLvOv|0Q7ArXJfHO(NbN$JSE zv_5Q9<57-dJ9nTwHg>n+?}Hn0XP~q-FhZf#^$lz_!DVVS``W z$lZxy-8q?;Kn_c~qpu?a!^VE@KZ&eBK3|DzL`iUdT1k0gQ7VI5W_oVoyp7Y6fx41C zT^vIy7~fu5DA~jy!EjOjyP?FK+gYa49BUjFZeZg1^zLlIeck!*-%H*2{Z-~>vXeGL z4C4bXhH!=*Ob0|6)-e>YHVm{--p&5cQa9@Fx?N)&1g^mdlDmI)a6OoK`OiCGL@;=| L`njxgN@xNARMw6a literal 192 zcmeAS@N?(olHy`uVBq!ia0vp^4Is?H3?#oinD`S&u?6^qxHc}^ck%H@$HxNwKoQOY zkH}&M25w;xW@MN(M*=9A;OXKRQgQ3;1w%mw1`g(pkM5f&Ss6?SpYVH0&M79K0z9CA iZ-W3HX$FP|wh4?iKNJ{qdw7q66neV)xvX``W z$lZxy-8q?;Kn_c~qpu?a!^VE@KZ&eBK3|DzL`iUdT1k0gQ7VI5W_oVoyp7Y6fx6s0 zT^vIy7~ftp6l`!1VA#m4YZCI~y2C-9wtuZDixrQT+Gi*H|K9gz&IPUpZH9FWJD47f eGSCAdkD0-P<+)T%%mZLpGkCiCxvXc}*5j~)%+dJhrBP8XZxj|ySZEa~AX+#@OYERiruLRs2}Li(O)@=#Vq{paH#N(5 zhq8;O$I8aAI(}e25?c^dGEwONbdjhd6$N+t%fHK&zh}r_d`M(*Hd8=Iu>a9yC$sHZ zKjH-)^nVE+>NvrpJ2CFefzq10hYw%ht#Q7{{`+%|lapRiq_4D4E z!$!|_&v`997WMs!KF_nOQ%$aC@+tSJs!V8M;53~Q*x0GDR!fsDXp4ODNB*1tRCcNq zO+9#GV}q7R$2R#t{B`BoBFog~E=k~dQMYkTf8c@r+$OK5eXY%jJS%+QJ5S8Y-1@DI zXX+-!0CAor~%sh6Pi}Y?y>R+}!fZ^Wx7qg2F9MBbrD6YzW`}hmv ziGc9a<_8Wc8iXx*Q#!r;d&#$N?`MY9+ee-)nw#pZWde*Awj^(N7a$D;Kb?2i11Zh| zkH}&M25w;xW@MN(M*=9wUgGKN%KnmzQ&3Rz*ovdSfI^Zbt`Q~9`MJ5Nc_j?aMX8A; zsVNHOnI#zt?w-B@;f;LaKt=yOT^vIyZoR$YD0skugT+y=Y2pFqy%jq;JnBlUBeJ?C z&omcXz3S}e$DAw;3<3;H4h$R&j0y}awBa!QqDG;M|ZZoW1NXHHJ`}Zy$IHGC6Bb6EAuP+amyMTs~c1KQ*K2f zA0p~a-k6&)()c>z3vN!JS{iSRLA125E~sTeK&hCuJ?XS0ttuP5yeE25t`}xSoGkg9 zG0s2)mnnJQmAb3%B#D<&iZq51D0~)qsaD{9P|t;BrgbN)WNR()dGuPNvR`U>*&8LL z7t%ZA9ONq{-ki!HTFy&uTvbPHfd!aEYKFDD9P;# z3|Y)nk}H$g#EGEyWdl1dV>X*`bUM{T4ev4IZ}@bT%`Yit%4#X>CSZ9aUJzVNkZkxOb$HIcC*7hv!#!xJrk;6EDWG?RY*v;0Qccu@y zGg(;k`)90(EK99U^~~|i!W`Ga&?V))aqegOE^kV?SOVYKqS7<%`&>>x^{^b$+6Q-; z#E5pxk<1)t)-F`sstWj4QQ+&D``(T4-PG;M{=YR~`L}(ddSGhLv_DjA=X&l@h5cmW zHAfPj?CMm<-}&ey+oUAlrhgTivEbfyd0LjeK)hNN4wmk8ph9j-1lzm$EhlWx!1$l+ z&JFd^`uE!rq+o^fDMQjRqCt1Wqv|}(fXB$4KF*%0fusETFsM}4AuFrw*VehT75B>d zrp6)lFXYl7PFXc{J*Y76k3PAFY;_gqVc86-DvbIkevBGj7m?vT`p?n$#dlu41>pz7 z?^SN9s^i}6Vg(HHP^wQo4y8M$KM%V%J~V5H6zpLKPfdG--n*i>!;eV!LSgHRic!*Y zlj5{O{P0sNz{Jm^ugO)}J^8)P?90K{dsq;GcGQFsBB~SOW1@kc|8PY&$W+;ntTqEX zTdvU)HGMotNgUwV!M!uN*!Pc%ndLxVs-Z)^J{+AVBDy&D8r@EI){m6b4A>fogx*%` zvSQ6UfGyE@#R*s1#`|@@jvMYlqmf1*w-xlQLt!7Ak6l8PCn9UR^ivC-ibmlK+Tv^O zSBLtR4>QxnHVM28m?ct>O0Sm)44y{1KbV<5YTbP0O(z{n=$F?q2=-sIC9<~D-^Npg z7>hCPxF*~UDM+WcjIH0_xE}SaIIMj4=joc5^EEaNdBeI_eofK6G-+RAennjHls4n3 zEGR}XiLpW^bva){uW0hG13%P|f-HK4!v3;q=_JL-zs$&88MMV7Zj#-B+VwJPs}d52)8-bvob`pxP;5oyXZGc3S*P|Q&PH6qPM*_Aqb>5dj!z8v4=Ubxhwp7`a(qyxTuBjndmjQ(r5gL& zA&}2FvlF&Uc$b)lsjIZ65JJ=-Dj+7WV+evFUX`kA*ma^_<(xeeo8C%3tz$E6II@LC zTL~cy&N8cYEFBxc?M3r0?0S)V^=G9@+>=3XC2{StVVa|h*E)@qA zfPI^Arqkpvd*O;DG8DIVp-6q6b4Ja4^w74YTwo!H?>n{j53dEE?xdSLaQ?H{3f$^%5RbArfj84=#A{1-F*Gg znz~t`4Z$3mcCC2jW-~aOcH{}u=oSRLEtl4lkJr2{`G3y3$m=dO@TiWNlNU=)G{UX#)LJlF9NH{*X4I>~;MVLU z3%D^aCOpyU^iar+Sca{N-S?q}m(x-E85+r~Few<7j%OAtEJF%y_cY9)B@R0Fa9&O> zm%sNQ^gH0}Q1AyS0ad};xlI2H3GnNga&h1P|I#i9{%&;e9s0X<`1{;4|NRB`tN zwO~T=D2m<(&?(~Gd#|pJLPdj~eF4KYqdk`k?f9(n4tNjMk7k*a+IeYq3^+gYXwl!e^u8Dl_NZz+A zTIK#1JY!VhyazZiN?+t9rqo{c03f)7SJ&|iGj^$L=@VF6inUmGsdhC5?(v z)y?H7-a}ZRj*00;{L#S@4D))ui1a=m+7|XvwOy;69@eXj}U0I#r*L2%kX3|z-j=1*~5lT6g_|WF=1Z8105emSh~p% zK0^`m;GF3favHFo&$)b%(f(-I9zTk3AAHm$d;%kf6lBs>YO0bLrnnr;=a!~8R&mti zw<~$Nu%E5m(WvCX%Ot_$w!E3zT!&ynteybjVI zo0~+g3dxZQ`>FZyix9%Ih)?zxb|U$vI>nkl`Wl_3a8Gv#ha4T`%N#9^b&RWoMk(zx z_k0`MZdSPMNqyVyg(u1m=wD%kU4g2RA8<4JW`)OtcKP)|yqiRomAg``(*jS2N?oMr z-7sOqR{~5TW6(jywU1{t_t|v)b4H3~ib?YyzWR`#W6A7gwT4*L(@P^&Nz!O^qH*o) z&)xxLjaws<|GZi%@8>7n9-u(H^+sm(A>!Jl>lCBnyF;&kf3>O6DEH^`Mw6)<`qxRq z#FPrnu5lF^2Te2yMVmkkz+zb8OrY-iGU?ckkpJ-ukZ7ieq5?x$r0M`lojD#ynYixG gj7!vjwP>w_*+eVJaJ4!I;0G0ibw1*B-!UZZKN=)V4gdfE delta 4495 zcmY+Ic{~%~|G?35%S|dr48@!ssF*Z4Gr2NX5{9`qWXYH#rOT0X4AGpCGvv$?W6Erf zzKF0`XpXX4{MPrM-|zRw^Lnn&^L$>P*Yn5w{d&@5N>rZ7>PPeQa&d9p{6Krh=>)

CVi05h7CM;*%6n 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 Ra6VVKtjz5ougtu${{s@(*f;``W z$lZxy-8q?;Kn_c~qpu?a!^VE@KZ&eBK3|DzL`iUdT1k0gQ7VI5W_oVoyp7Y6fx6~< zx;TbZFuuKO$k*f`;1FnX@5G@)%{QWo{kgf%w=|{*tK56=H+;s{IS<)S+1k$gY}8sR z*?#`FY391^zt7iavL}E0{7c#H&(*rTT=wL9pGBe=A8;{*GwfhGAj+_gp@6kPn;~Yv zgmfDo>)z`6d*>G4u|3O=7soBz@QdYv-eZGzlc&qX|Gva`?9FXyhngR&Au4K_2H3gq lFnj&$hkd%^_2cQES+&KrlU^6bSpXxE!PC{xWt~$(698@{v*Q2& literal 0 HcmV?d00001 diff --git a/examples/starting-3d-car-racing/starting-3d-car-racing.json b/examples/starting-3d-car-racing/starting-3d-car-racing.json index 0613f47f5..ca9f97cd9 100644 --- a/examples/starting-3d-car-racing/starting-3d-car-racing.json +++ b/examples/starting-3d-car-racing/starting-3d-car-racing.json @@ -1,7 +1,7 @@ { "firstLayout": "", "gdVersion": { - "build": 272, + "build": 274, "major": 5, "minor": 6, "revision": 0 @@ -109,14 +109,6 @@ "smoothed": true, "userAdded": false }, - { - "file": "assets/Flat dark joystick border.png", - "kind": "image", - "metadata": "", - "name": "assets\\Flat dark joystick border.png", - "smoothed": true, - "userAdded": false - }, { "file": "assets/Orange - Sedan Sports.glb", "kind": "model3D", @@ -213,6 +205,30 @@ "name": "assets/Skybox_Right.png", "smoothed": true, "userAdded": false + }, + { + "file": "assets/StartingWall.png", + "kind": "image", + "metadata": "", + "name": "assets/StartingWall.png", + "smoothed": true, + "userAdded": false + }, + { + "file": "assets/Flat dark joystick border vertical.png", + "kind": "image", + "metadata": "", + "name": "assets/Flat dark joystick border vertical.png", + "smoothed": true, + "userAdded": false + }, + { + "file": "assets/Flat dark joystick border horizontal.png", + "kind": "image", + "metadata": "", + "name": "assets/Flat dark joystick border horizontal.png", + "smoothed": true, + "userAdded": false } ] }, @@ -234,7 +250,7 @@ "title": "", "v": 255, "uiSettings": { - "grid": true, + "grid": false, "gridType": "rectangular", "gridWidth": 64, "gridHeight": 64, @@ -244,7 +260,7 @@ "gridOffsetZ": 0, "gridColor": 10401023, "gridAlpha": 0.8, - "snap": true, + "snap": false, "zoomFactor": 0.49508400737491426, "windowMask": false, "selectedLayer": "", @@ -407,24 +423,6 @@ "stringProperties": [], "initialVariables": [] }, - { - "angle": 0, - "customSize": true, - "depth": 2, - "height": 1024, - "keepRatio": true, - "layer": "", - "locked": true, - "name": "FinishLine_3D", - "persistentUuid": "242e38cc-5ccc-4213-8807-aee00c5f77a1", - "width": 128, - "x": 1536, - "y": -192, - "zOrder": 12, - "numberProperties": [], - "stringProperties": [], - "initialVariables": [] - }, { "angle": 0, "customSize": true, @@ -897,6 +895,22 @@ "numberProperties": [], "stringProperties": [], "initialVariables": [] + }, + { + "angle": 90, + "customSize": true, + "depth": 2, + "height": 128, + "layer": "", + "name": "FinishLine_3D", + "persistentUuid": "1db3deb5-1e30-4d46-8313-ada820f56bd8", + "width": 1024, + "x": 1088, + "y": 256, + "zOrder": 12, + "numberProperties": [], + "stringProperties": [], + "initialVariables": [] } ], "objects": [ @@ -1039,15 +1053,18 @@ { "assetStoreId": "cddab55130dbd5a0b36f763200e876fb514aa608e1b057a136ebdb5f80006e2c", "name": "PlayerCar", + "persistentUuid": "191c2cfe-430f-4fdb-9f74-2d8cb2fcd734", "type": "Scene3D::Model3DObject", "variables": [ { "name": "LastCheckPoint", + "persistentUuid": "1940c891-b587-424a-95ed-90a80ca00807", "type": "number", "value": 0 }, { "name": "LapNumber", + "persistentUuid": "998b192e-cbb7-41bf-858e-e06bcca957a6", "type": "number", "value": 0 } @@ -1055,6 +1072,7 @@ "effects": [], "behaviors": [ { + "isFolded": true, "name": "Physics3D", "type": "Physics3D::Physics3DBehavior", "object3D": "Object3D", @@ -1083,6 +1101,7 @@ "massOverride": 1500 }, { + "isFolded": true, "name": "PhysicsCar3D", "type": "Physics3D::PhysicsCar3D", "physics3D": "Physics3D", @@ -1113,6 +1132,7 @@ "pitchRollAngleMax": 89 }, { + "isFolded": true, "name": "PhysicsCar3DKeyboardMapper", "type": "PhysicsCar3DKeyMapper::PhysicsCar3DKeyboardMapper", "PhysicsCar3D": "PhysicsCar3D", @@ -1186,7 +1206,7 @@ "behaviors": [], "content": { "DeadZoneRadius": 0.2, - "JoystickIdentifier": "Primary" + "JoystickIdentifier": "Secondary" }, "childrenContent": { "Border": { @@ -1203,7 +1223,7 @@ "sprites": [ { "hasCustomCollisionMask": false, - "image": "assets\\Flat dark joystick border.png", + "image": "assets/Flat dark joystick border vertical.png", "points": [], "originPoint": { "name": "origine", @@ -1264,13 +1284,14 @@ { "assetStoreId": "e71bd69f896d6c7531b48c65ceb5da25071d4fbdeb518aeceecba8d21f34ed8d", "name": "Steer_Joystick", + "persistentUuid": "340cca5d-d51e-4e7b-ad28-bc0dc6bcf73e", "type": "SpriteMultitouchJoystick::SpriteMultitouchJoystick", "variant": "", "variables": [], "effects": [], "behaviors": [], "content": { - "JoystickIdentifier": "Secondary", + "JoystickIdentifier": "Primary", "DeadZoneRadius": 0.2 }, "childrenContent": { @@ -1288,7 +1309,7 @@ "sprites": [ { "hasCustomCollisionMask": false, - "image": "assets\\Flat dark joystick border.png", + "image": "assets/Flat dark joystick border horizontal.png", "points": [], "originPoint": { "name": "origine", @@ -1603,14 +1624,14 @@ "height": 128, "depth": 128, "enableTextureTransparency": false, - "facesOrientation": "Y", - "frontFaceResourceName": "assets\\StartingFinishLine.png", - "backFaceResourceName": "assets\\StartingFinishLine.png", + "facesOrientation": "Z", + "frontFaceResourceName": "assets/StartingWall.png", + "backFaceResourceName": "assets/StartingWall.png", "backFaceUpThroughWhichAxisRotation": "X", - "leftFaceResourceName": "assets\\StartingFinishLine.png", - "rightFaceResourceName": "assets\\StartingFinishLine.png", - "topFaceResourceName": "assets\\StartingFinishLine.png", - "bottomFaceResourceName": "assets\\StartingFinishLine.png", + "leftFaceResourceName": "assets/StartingWall.png", + "rightFaceResourceName": "assets/StartingWall.png", + "topFaceResourceName": "assets/StartingWall.png", + "bottomFaceResourceName": "assets/StartingWall.png", "frontFaceVisible": true, "backFaceVisible": true, "leftFaceVisible": true, diff --git a/examples/starting-3d-draggable-tiles/assets/StartingTiledGround.png b/examples/starting-3d-draggable-tiles/assets/StartingTiledGround.png index 7e2f4217b7034fb750d96e373daa7e017c8fa5e0..380baea6566db151e41f861a43578cc7cbc510e4 100644 GIT binary patch delta 301 zcmeyw`kHBivIPTMlDE4H!+#K5uy^@n1_lPs0*}aI1_nK45N51cYF`EvWH0gbb!C6W zE-EO;)ahR5JNXTxM*U?^7srr_TW@dqavd@dIr8yj_Jq2CT?G&SzE|^bJQ}oCY1Z>= z!YbW=o-+y8&E9^Z<#El|8#DNRds-G--Jdeady?0;*^w7+Y(F(O^M#0X@XLP>-mU9O zUS$Kc4j;HO$s%5?*LnHA%)6<&_u}$(zx#?g-TZgnJTr3pn@_JFubdLI^tbP%Gfv;w zm+bd{IqB~k<{RtfEKWMEjK^oBX>KKmaAw(Hp|Quduv0x9=&^>bP0l+XkKr$lXI delta 645 zcmV;00($-H0`dirBYy#fX+uL$Nkc;*aB^>EX>4Tx04R}tkv&MmP!xqvQ?*hm4t5Z6 z$WV2$AS&W0RV;#q(pG5I!Q`cX(4-+rad8w}3l9D)RvlcNb#-tR1i>E=cfm=~MM}Ic zDYS_3!sC6MchBLy`v9R)VXE0R0jQc~q~b9#n_CqFuL#125P!zdBQaA?q!%;rtgn0O zrMipqEbqQQt53b4)iZ;tk@NO-tvzPaI|?Ng+Nb9yjQM#E)E;U4G+SbXee- zVIz~8Ck_*f#SWG`n3W8bc$zq(s2b%9S(g>gTb$KOjkWH{Ul_`3D`~FN8b%yTNFWIj zGHNKJ0t->vHGfh}r06{6;U9ARNpi{LDua<@0ad7w96$IU{GP2@n3{BxLNTEG#kN02 zfWR)$tlReYv28a`0RJ;^rM3Ol1~B_cdcCbhj)4Ac;NrTi$$P-%4lwwnONQh~ep*7I z0KA{kH|2nVTOhdR&aHKh(+40!vs%6Z4i16QB4w|8yiB{db8i3kwC48%K-+SYlBK{u z00006VoOIv00000008+zyMF)x010qNS#tmYE+YT{E+YYWr9XB6000McNliru=?Vc1 zIssHCh^3KRC{D>qL_t(|+U?pQ4#gl8M9~2QqCr%2|68m10zq1e0L9-0PjF`gLk@<7 zaliYXAx53orPV1pYxFM5e)qlaJW`TXTCK9|cXQ6EjpMa>lQ97uHURbkXtkOiz`Eam z&!dvFNqNax*8N_RM(@jSqSdO=yR7@YB!w84e^DPPDa0u2e)a+E19%6MF##PQ0Bi!l fK7f6|-ur+*Ha~vqE~N<*00000NkvXXu0mjfuiqby diff --git a/examples/starting-3d-draggable-tiles/assets/StartingTiles.png b/examples/starting-3d-draggable-tiles/assets/StartingTiles.png index f3a54a2d4c682d8e054168774f7a9b233356b57a..f29984053f4428559dbb1b669eb6e57589a7ef55 100644 GIT binary patch literal 488 zcmeAS@N?(olHy`uVBq!ia0vp^4Is?H1|$#LC7uRSoCO|{#S9GG!XV7ZFl&wkP>``W z$lZxy-8q?;Kn_c~qpu?a!^VE@KZ&eBK3|DzL`iUdT1k0gQ7VI5W_oVoyp7Y6fx41C zT^vIy7~fu5DA~jy!EjOjyP?FK+gYa49BUjFZeZg1^zLlIeck!*-%H*2{Z-~>vXeGL z4C4bXhH!=*Ob0|6)-e>YHVm{--p&5cQa9@Fx?N)&1g^mdlDmI)a6OoK`OiCGL@;=| L`njxgN@xNARMw6a literal 969 zcmeAS@N?(olHy`uVBq!ia0vp^4Is?H1|$#LC7xzrU~J8Fb`J1#c2+1T%1_J8No8Qr zm{>c}*5h!1NUMMF)*vmuGJ(Pq0i_!iv>s`M>NK@p3d^{9@X6Bp3riF|3kyYJxcUC+ zgvrLtDJ?A!WjQD>P=3(!qN~>)ea$T$9}hjQol}1P;ojd23j-5Z-85xfF)MY+BSXX4 z(W1=jIS#c89J`=w80^19-1x(q_0_MI?&|)e^M0TGYX6h7c|LR=DfThfdBpo@rmy$& zJ^nIjs-B8=>T>2UnvV3xOpmR598(hUU}mE78@e-%9wKLaUmU5Hv_E6vyG|JLg%)}jd%QVPm$qSoj59N#gcov;} zqSZVxnK5+51R47u@_**7jhr#7WU`atg5O8C*}F9y(7Uka_P6gFbItu3{u>u8-TL<` zo563-s=aGn`x^e`K6tci?aAr`Px%afEb(r8;!zjnw2)!X{EOyK8yce(c04<`wzTgu zyMpuPsC|rle2o{CQrB1S*}c6u@Bg`sfOD;wpYt=&gSW}>oWQl7;iF1B#Zfaf$gL6@8 zVo7R>LV0FMhJw4NZ$Nk>pExkS7kIijhE&{od)F}Ukb{83#X_kV0q&&_{)HQ}PTaO3 zp>X56IZEXT8=vjv?rGVVy6witg89#FCt3F<|A|?jKY#ZWq3G<`=lnOBf9#8SWaE4H z+4`+zbN^5M`||zI$MuK)d|^ycXJAxdU}<0wU|@1!;9y|H&5^krmHq1HOXdn&xs6jU zrG5+Y`u;PHQE=ndZ{O4l*P3p-w(GpO!=Lx;72kF5el|IpDYN_hcKHK!4R_wKA21Za zV=#)9GJSs;PTu5qc=eazaP7SB^X{*|AJu*Dv$1sC`RDU~*8O?^cG_?Mgm1q;`Z3~l zEs~)P%xn|>*46xC*mS@DJ6Fe@?~hppYWK(g=lpEE8R$TU-;6TMf(%Rqybm>yog3(- jCyY1vABZN{J0#UJZ1|pbv1;cL1|aZs^>bP0l+XkKk$`vk diff --git a/examples/starting-3d-draggable-tiles/assets/starting_woodthump.wav b/examples/starting-3d-draggable-tiles/assets/starting_woodthump.wav new file mode 100644 index 0000000000000000000000000000000000000000..5949eb5a8c6f3abf7f87d9f10ed3a83be9ee84b9 GIT binary patch literal 35904 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≺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 L`njxgN@xNARMw6a literal 192 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 delta 4495 zcmY+Ic{~%~|G?35%S|dr48@!ssF*Z4Gr2NX5{9`qWXYH#rOT0X4AGpCGvv$?W6Erf zzKF0`XpXX4{MPrM-|zRw^Lnn&^L$>P*Yn5w{d&@5N>rZ7>PPeQa&d9p{6Krh=>)

CVi05h7CM;*%6n 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 Ra6VVKtjz5ougtu${{s@(*f;``W z$lZxy-8q?;Kn_c~qpu?a!^VE@KZ&eBK3|DzL`iUdT1k0gQ7VI5W_oVoyp7Y6fx6~< zx;TbZFuuKO$k*f`;1FnX@5G@)%{QWo{kgf%w=|{*tK56=H+;s{IS<)S+1k$gY}8sR z*?#`FY391^zt7iavL}E0{7c#H&(*rTT=wL9pGBe=A8;{*GwfhGAj+_gp@6kPn;~Yv zgmfDo>)z`6d*>G4u|3O=7soBz@QdYv-eZGzlc&qX|Gva`?9FXyhngR&Au4K_2H3gq lFnj&$hkd%^_2cQES+&KrlU^6bSpXxE!PC{xWt~$(698@{v*Q2& literal 0 HcmV?d00001 diff --git a/examples/starting-3d-driving/starting-3d-driving.json b/examples/starting-3d-driving/starting-3d-driving.json index 7ee464b4c..2127383ad 100644 --- a/examples/starting-3d-driving/starting-3d-driving.json +++ b/examples/starting-3d-driving/starting-3d-driving.json @@ -1,7 +1,7 @@ { "firstLayout": "", "gdVersion": { - "build": 272, + "build": 274, "major": 5, "minor": 6, "revision": 0 @@ -109,14 +109,6 @@ "smoothed": true, "userAdded": false }, - { - "file": "assets/Flat dark joystick border.png", - "kind": "image", - "metadata": "", - "name": "assets\\Flat dark joystick border.png", - "smoothed": true, - "userAdded": false - }, { "file": "assets/Orange - Sedan Sports.glb", "kind": "model3D", @@ -192,10 +184,26 @@ "userAdded": false }, { - "file": "assets/StartingFinishLine.png", + "file": "assets/StartingWall.png", + "kind": "image", + "metadata": "", + "name": "assets/StartingWall.png", + "smoothed": true, + "userAdded": false + }, + { + "file": "assets/Flat dark joystick border vertical.png", + "kind": "image", + "metadata": "", + "name": "assets/Flat dark joystick border vertical.png", + "smoothed": true, + "userAdded": false + }, + { + "file": "assets/Flat dark joystick border horizontal.png", "kind": "image", "metadata": "", - "name": "assets/StartingFinishLine.png", + "name": "assets/Flat dark joystick border horizontal.png", "smoothed": true, "userAdded": false } @@ -219,7 +227,7 @@ "title": "", "v": 255, "uiSettings": { - "grid": true, + "grid": false, "gridType": "rectangular", "gridWidth": 64, "gridHeight": 64, @@ -229,11 +237,11 @@ "gridOffsetZ": 0, "gridColor": 10401023, "gridAlpha": 0.8, - "snap": true, - "zoomFactor": 0.6244537826600177, + "snap": false, + "zoomFactor": 0.4633111824514118, "windowMask": false, "selectedLayer": "", - "gameEditorMode": "instances-editor" + "gameEditorMode": "embedded-game" }, "objectsGroups": [], "variables": [], @@ -970,7 +978,7 @@ "behaviors": [], "content": { "DeadZoneRadius": 0.2, - "JoystickIdentifier": "Primary" + "JoystickIdentifier": "Secondary" }, "childrenContent": { "Border": { @@ -987,7 +995,7 @@ "sprites": [ { "hasCustomCollisionMask": false, - "image": "assets\\Flat dark joystick border.png", + "image": "assets/Flat dark joystick border vertical.png", "points": [], "originPoint": { "name": "origine", @@ -1055,7 +1063,7 @@ "effects": [], "behaviors": [], "content": { - "JoystickIdentifier": "Secondary", + "JoystickIdentifier": "Primary", "DeadZoneRadius": 0.2 }, "childrenContent": { @@ -1073,7 +1081,7 @@ "sprites": [ { "hasCustomCollisionMask": false, - "image": "assets\\Flat dark joystick border.png", + "image": "assets/Flat dark joystick border horizontal.png", "points": [], "originPoint": { "name": "origine", @@ -1228,13 +1236,13 @@ "depth": 128, "enableTextureTransparency": false, "facesOrientation": "Y", - "frontFaceResourceName": "assets/StartingFinishLine.png", - "backFaceResourceName": "assets/StartingFinishLine.png", + "frontFaceResourceName": "assets/StartingWall.png", + "backFaceResourceName": "assets/StartingWall.png", "backFaceUpThroughWhichAxisRotation": "X", - "leftFaceResourceName": "assets/StartingFinishLine.png", - "rightFaceResourceName": "assets/StartingFinishLine.png", - "topFaceResourceName": "assets/StartingFinishLine.png", - "bottomFaceResourceName": "assets/StartingFinishLine.png", + "leftFaceResourceName": "assets/StartingWall.png", + "rightFaceResourceName": "assets/StartingWall.png", + "topFaceResourceName": "assets/StartingWall.png", + "bottomFaceResourceName": "assets/StartingWall.png", "frontFaceVisible": true, "backFaceVisible": true, "leftFaceVisible": true, diff --git a/examples/starting-3d-endless-runner/assets/Background.png b/examples/starting-3d-endless-runner/assets/Background.png index 10a4d95ac58d625c183f9990dfaabb6a99d7a368..313afed01e19a3496c7e7147c0c31af83fba1bd8 100644 GIT binary patch literal 381 zcmeAS@N?(olHy`uVBq!ia0vp^4Is?H1|$#LC7uRSoCO|{#S9GG!XV7ZFl&wkP>``W z$lZxy-8q?;Kn_c~qpu?a!^VE@KZ&eBK3|DzL`iUdT1k0gQ7VI5W_oVoyp7Y6figcl zT^vIy7~kI6D9Y$4!g|48%RxzDfsv_(<5CuFg$St)+0OzFpLti9Z}fZdcHTYHcU9Xj z>v&zZ+0MoF?&A7m5~n}u?vMSaw0!@)!v%e(+P)W@Iv-m1xCUr40wl!cegAjKcHZ$7 zYf`4Qt@^X==gKRGKmWazdu{a!jpuQnHvgadN&2b&sfnNXU2A@lJ`mkqwgfboFyt=akR{00FX$VgLXD literal 277 zcmeAS@N?(olHy`uVBq!ia0vp^4Is?H1|$#LC7uRSoCO|{#S9F3${@^GvDCf{D0tk{ z#WAGf*4sNfc^eD_SRAImyOeR1GnZi+$BKr_ndvH%^P1+Ty_g}@q62(P|M33*i=X8jDHaug<3Bf_ zl#2#nVLXchU}ZcD4q$0K3kqOuJPQV3aXbqGV0Ap}3}AUY>jYqZJZlZ$VLWRE;AK2( z4B%-zYXsnJJb$YV;Bh>w1>ki&I|<-{u5L{IQcB#9ii6Kpl5{I?*qId zGJEa5rW?l zvW5`lmY5}kX!iuIAVj?>N`$A;6;f`1$h}?Opd1WQo?jrvdbutBI2EX>4Tx04R}tkv&MmKpe$iQ^hJ31v`j1 zWT;LSL`8IyDi*;)X)CnqU~=gfG-*guTpR`0f`cE6RR-peBHyx z*SiSM@;>+H=v8ti1AHR!9McVpc!PL))6zNb6Ngw)Qi#uq#|*k4@gvt|m)|%S92R(H z$VeyWi9^IW<^6Ko+1t_sz&)j#$|=`7H73wWzBo?7Y1|MN{Z_=hY`aP;z&S* zj4Dbf!$O2sjeisqN!pKk_=g;Sf?P7WN?_zzKm{r!#}EDozq>W_lM`-IFbZ_O*!IT= z(6tLRYPS7-Y}<_!!2b+fX)S-H4$OR#UTbOLBcN{^xVUa<@*Z%x0}MXtk|8;gpQez{ z1Mg?_O<7>z7U)@Xdu#6F^Z`gySIalR!67hOpzL*zcYg=kd;9lHv%epeadNd+4eVL~ z000JJOGiWi{{a60|De66lK=n!32;bRa{vG?BLDy{BLR4&KXw2B00(qQO+^Rk2?!7; z5&&V!DF6Tf8FWQhbVF}#ZDnqB07G(RVRU6=Aa`kWXdp*PO;A^X4i^9b0YynfK~#9! z?c2=_f`32|1>m>FUTnY`EW(5Kp&r#fY=aiG0c{2^m|%ou|47{TW-i2w$@_$1b_EBZ zMX$a-2-E`NWdH_X00v+H24DcXwVdU<)4wz!*LlfHiy&0CV_B z0QT@+0EO_Q04m{ufgPbMfER!pH0tLJa6;h+0S?eA`(I$F2S@|70QTxVU<=@)+ygcM z)*=X_O;82FdlyhZn7j?LBLvL>%m~4Q5Gz8|B*=&mJqoiSB&`LQ5Rw-|EC^YvK?a2E zQwSAv{$8YP%d}ds1}M9nanIUmufmu(!sv%K!|( h01UtY48Q<%>kVL6M+RcBz9Rqt002ovPDHLkV1i)OU+(|_ diff --git a/examples/starting-3d-endless-runner/assets/StartingCollision.wav b/examples/starting-3d-endless-runner/assets/StartingCollision.wav new file mode 100644 index 0000000000000000000000000000000000000000..5b89dda9865db53229ee501b111b0612758f968b GIT binary patch literal 18870 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 Q0wy2^Pgg&ebxsLQ0O86PF#rGn delta 1374 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 delta 895 zcmV-_1AzRq9G(Y|EPoOJ01FZU(%pXi0009}NklV=8pn03oHq-XT~2RxmbyHoFDegcsw4W zh*1WE!QXzr|G(4eB<*%P?RLBCG7X>2+Z6wcJ|bTPFN+t?fqyM?5Qko`m+5He3w@&R zMx*h^DQI}1{N>`j?Q{Vv0d$RLx-i?){6$Vw;C?nd8|uNTWsbpO25>=p)$nDC28pH0 zJ>$yENRfK7*=+vPX%q-_Lw|%D;37N~qA^#;6`jYT6Mcba3?eq_zQ9u;F1`2jG(cR`g%|q@Pk|T* zi_2k|B}P;j_s;7|@UTY=;6B_p7cR>wKzm%urCb}t^a8NgLD{#4L&{*&G|jf#?XBT& z3FG?Jpy2#K!XU@varR6kKm;Svx)1Qq=X3T-=JUBsU4JOSX?_P!5XATyz?}{7t}>g= zSc46sj7L>?5PTm0C&HL;I?kWKlK}C~vE~X_)q%$i@h;qDD?Dz9w?iVE;c-H|970j+ zKNIk{AYSw!<=)pMJQd<-G@{!ZQ^ox#;9X_E-)F<&kS0z@0gw_NCBzFr7vWJtGzt)d zKOT=6r+=7+XcXXTwaO$2;)zxQlq3P5j{rBDjR<&9B~}8QOeSm=SQV4IlgM;(I3U6j zatJ&CJl|xpI(r6!Wc^mkk>*Zavl-weCdhr1X*s}z{WQ*UfH6xz)fIrZto{J7Mo6DK z2ijJEcZW)x$C|bRygSz9JXp6C;6s)cS89ii0Dm7+J2;oIjR2pf`yyp404&#DruuSR zszJ24?lGpA%Qqax1ksW@=O)y-f^YAP_=5.5.222", + "helpPath": "", + "iconUrl": "data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iVVRGLTgiPz48IURPQ1RZUEUgc3ZnIFBVQkxJQyAiLS8vVzNDLy9EVEQgU1ZHIDEuMS8vRU4iICJodHRwOi8vd3d3LnczLm9yZy9HcmFwaGljcy9TVkcvMS4xL0RURC9zdmcxMS5kdGQiPjxzdmcgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIiB4bWxuczp4bGluaz0iaHR0cDovL3d3dy53My5vcmcvMTk5OS94bGluayIgdmVyc2lvbj0iMS4xIiBpZD0ibWRpLXZlY3Rvci1kaWZmZXJlbmNlLWFiIiB3aWR0aD0iMjQiIGhlaWdodD0iMjQiIHZpZXdCb3g9IjAgMCAyNCAyNCI+PHBhdGggZD0iTTMsMUMxLjg5LDEgMSwxLjg5IDEsM1Y1SDNWM0g1VjFIM003LDFWM0gxMFYxSDdNMTIsMVYzSDE0VjVIMTZWM0MxNiwxLjg5IDE1LjExLDEgMTQsMUgxMk0xLDdWMTBIM1Y3SDFNMTQsN0MxNCw3IDE0LDExLjY3IDE0LDE0QzExLjY3LDE0IDcsMTQgNywxNEM3LDE0IDcsMTggNywyMEM3LDIxLjExIDcuODksMjIgOSwyMkgyMEMyMS4xMSwyMiAyMiwyMS4xMSAyMiwyMFY5QzIyLDcuODkgMjEuMTEsNyAyMCw3QzE4LDcgMTQsNyAxNCw3TTE2LDlIMjBWMjBIOVYxNkgxNEMxNS4xMSwxNiAxNiwxNS4xMSAxNiwxNFY5TTEsMTJWMTRDMSwxNS4xMSAxLjg5LDE2IDMsMTZINVYxNEgzVjEySDFaIiAvPjwvc3ZnPg==", + "name": "ShakeObject3D", + "previewIconUrl": "https://resources.gdevelop-app.com/assets/Icons/vector-difference-ab.svg", + "shortDescription": "Shake 3D objects.", + "version": "2.1.1", + "description": [ + "Shake 3D objects with translation and rotation.", + "", + "The 3D racing game example uses this extension ([open the project online](https://editor.gdevelop.io/?project=example://3d-racing-game))." + ], + "origin": { + "identifier": "ShakeObject3D", + "name": "gdevelop-extension-store" + }, + "tags": [ + "3d", + "shaking", + "effect", + "shake", + "translate", + "rotate" + ], + "authorIds": [ + "IWykYNRvhCZBN3vEgKEbBPOR3Oc2" + ], + "changelog": [ + { + "version": "2.0.0", + "breaking": "- The behavior for 3D box has been removed. The other behavior can be used for both models and boxes." + } + ], + "dependencies": [], + "globalVariables": [], + "sceneVariables": [], + "eventsFunctions": [ + { + "fullName": "", + "functionType": "Action", + "name": "onFirstSceneLoaded", + "sentence": "", + "events": [ + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [], + "actions": [ + { + "type": { + "value": "ShakeObject3D::DefineHelperClasses" + }, + "parameters": [ + "", + "" + ] + } + ] + } + ], + "parameters": [], + "objectGroups": [] + }, + { + "description": "Define helper classes JavaScript code.", + "fullName": "Define helper classes", + "functionType": "Action", + "name": "DefineHelperClasses", + "private": true, + "sentence": "Define helper classes JavaScript code", + "events": [ + { + "type": "BuiltinCommonInstructions::JsCode", + "inlineCode": [ + "if (gdjs._shakeObjectExtension) {", + " return;", + "}", + "", + "/** Noise generator manager. */", + "class NoiseManager {", + " /**", + " * Create the manager of noise generators.", + " */", + " constructor() {", + " this.seed = gdjs.randomInRange(1, Number.MAX_SAFE_INTEGER);", + " /** @type {Map} */", + " this.generators = new Map();", + " }", + "", + " /**", + " * @param name {string}", + " * @return {NoiseGenerator}", + " */", + " getGenerator(name) {", + " let generator = this.generators.get(name);", + " if (!generator) {", + " generator = new NoiseGenerator(name + this.seed);", + " this.generators.set(name, generator);", + " }", + " return generator;", + " }", + "", + " /**", + " * @param seed {number}", + " */", + " setSeed(seed) {", + " this.seed = seed;", + " this.generators.forEach(generator => generator.setSeed(name + this.seed));", + " }", + "", + " /**", + " * @param name {string}", + " */", + " deleteGenerator(name) {", + " this.generators.delete(name);", + " }", + "", + " /**", + " */", + " deleteAllGenerators() {", + " this.generators.clear();", + " }", + "}", + "", + "/** Noise generator with octaves. */", + "class NoiseGenerator {", + " /**", + " * Create a noise generator with a seed.", + " * @param seed {string}", + " */", + " constructor(seed) {", + " this.simplexNoise = new SimplexNoise(seed);", + " this.frequency = 1;", + " this.octaves = 1;", + " this.persistence = 0.5;", + " this.lacunarity = 2;", + " this.xLoopPeriod = 0;", + " this.yLoopPeriod = 0;", + " }", + "", + " /**", + " * @param seed {string}", + " */", + " setSeed(seed) {", + " this.simplexNoise = new SimplexNoise(seed);", + " }", + "", + " /**", + " * @param x {float}", + " * @param y {float}", + " * @param z {float} optionnal", + " * @param w {float} optionnal", + " * @return {float}", + " */", + " noise(x, y, z, w) {", + " if (this.xLoopPeriod && this.yLoopPeriod) {", + " const circleRatioX = 2 * Math.PI / this.xLoopPeriod;", + " const circleRatioY = 2 * Math.PI / this.yLoopPeriod;", + " const angleX = circleRatioX * x;", + " const angleY = circleRatioY * y;", + " x = Math.cos(angleX) / circleRatioX;", + " y = Math.sin(angleX) / circleRatioX;", + " z = Math.cos(angleY) / circleRatioY;", + " w = Math.sin(angleY) / circleRatioY;", + " }", + " else if (this.xLoopPeriod) {", + " const circleRatio = 2 * Math.PI / this.xLoopPeriod;", + " const angleX = circleRatio * x;", + " w = z;", + " z = y;", + " x = Math.cos(angleX) / circleRatio;", + " y = Math.sin(angleX) / circleRatio;", + " }", + " else if (this.yLoopPeriod) {", + " const circleRatio = 2 * Math.PI / this.xLoopPeriod;", + " const angleX = circleRatio * x;", + " w = z;", + " // Make the circle perimeter equals to the looping period", + " // to keep the same perceived frequency with or without looping.", + " y = Math.cos(angleX) / circleRatio;", + " z = Math.sin(angleX) / circleRatio;", + " }", + " let noiseFunction = this.simplexNoise.noise4D.bind(this.simplexNoise);", + " if (z === undefined) {", + " noiseFunction = this.simplexNoise.noise2D.bind(this.simplexNoise);", + " }", + " else if (w === undefined) {", + " noiseFunction = this.simplexNoise.noise3D.bind(this.simplexNoise);", + " }", + " let frequency = this.frequency;", + " let noiseSum = 0;", + " let amplitudeSum = 0;", + " let amplitude = 1;", + " for (let i = 0; i < this.octaves; i++) {", + " noiseSum += noiseFunction(x * frequency, y * frequency, z * frequency, w * frequency) * amplitude;", + " amplitudeSum += Math.abs(amplitude);", + " amplitude *= this.persistence;", + " frequency *= this.lacunarity;", + " }", + " return noiseSum / amplitudeSum;", + " }", + "}", + "", + "/*", + "A fast javascript implementation of simplex noise by Jonas Wagner", + "https://github.com/jwagner/simplex-noise.js", + "", + "Based on a speed-improved simplex noise algorithm for 2D, 3D and 4D in Java.", + "Which is based on example code by Stefan Gustavson (stegu@itn.liu.se).", + "With Optimisations by Peter Eastman (peastman@drizzle.stanford.edu).", + "Better rank ordering method by Stefan Gustavson in 2012.", + "", + " Copyright (c) 2021 Jonas Wagner", + "", + " Permission is hereby granted, free of charge, to any person obtaining a copy", + " of this software and associated documentation files (the \"Software\"), to deal", + " in the Software without restriction, including without limitation the rights", + " to use, copy, modify, merge, publish, distribute, sublicense, and/or sell", + " copies of the Software, and to permit persons to whom the Software is", + " furnished to do so, subject to the following conditions:", + "", + " The above copyright notice and this permission notice shall be included in all", + " copies or substantial portions of the Software.", + "", + " THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR", + " IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,", + " FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE", + " AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER", + " LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,", + " OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE", + " SOFTWARE.", + " */", + "", + "const F2 = 0.5 * (Math.sqrt(3.0) - 1.0);", + "const G2 = (3.0 - Math.sqrt(3.0)) / 6.0;", + "const F3 = 1.0 / 3.0;", + "const G3 = 1.0 / 6.0;", + "const F4 = (Math.sqrt(5.0) - 1.0) / 4.0;", + "const G4 = (5.0 - Math.sqrt(5.0)) / 20.0;", + "const grad3 = new Float32Array([1, 1, 0,", + " -1, 1, 0,", + " 1, -1, 0,", + " -1, -1, 0,", + " 1, 0, 1,", + " -1, 0, 1,", + " 1, 0, -1,", + " -1, 0, -1,", + " 0, 1, 1,", + " 0, -1, 1,", + " 0, 1, -1,", + " 0, -1, -1]);", + "const grad4 = new Float32Array([0, 1, 1, 1, 0, 1, 1, -1, 0, 1, -1, 1, 0, 1, -1, -1,", + " 0, -1, 1, 1, 0, -1, 1, -1, 0, -1, -1, 1, 0, -1, -1, -1,", + " 1, 0, 1, 1, 1, 0, 1, -1, 1, 0, -1, 1, 1, 0, -1, -1,", + " -1, 0, 1, 1, -1, 0, 1, -1, -1, 0, -1, 1, -1, 0, -1, -1,", + " 1, 1, 0, 1, 1, 1, 0, -1, 1, -1, 0, 1, 1, -1, 0, -1,", + " -1, 1, 0, 1, -1, 1, 0, -1, -1, -1, 0, 1, -1, -1, 0, -1,", + " 1, 1, 1, 0, 1, 1, -1, 0, 1, -1, 1, 0, 1, -1, -1, 0,", + " -1, 1, 1, 0, -1, 1, -1, 0, -1, -1, 1, 0, -1, -1, -1, 0]);", + "", + "", + "/**", + " * Builds a random permutation table.", + " * This is exported only for (internal) testing purposes.", + " * Do not rely on this export.", + " * @param {() => number} random", + " * @private", + " */", + "function buildPermutationTable(random) {", + " const p = new Uint8Array(256);", + " for (let i = 0; i < 256; i++) {", + " p[i] = i;", + " }", + " for (let i = 0; i < 255; i++) {", + " const r = i + ~~(random() * (256 - i));", + " const aux = p[i];", + " p[i] = p[r];", + " p[r] = aux;", + " }", + " return p;", + "}", + "", + "/*", + "The ALEA PRNG and masher code used by simplex-noise.js", + "is based on code by Johannes Baagøe, modified by Jonas Wagner.", + "See alea.md for the full license.", + "@param {string|number} seed", + "*/", + "function alea(seed) {", + " let s0 = 0;", + " let s1 = 0;", + " let s2 = 0;", + " let c = 1;", + " const mash = masher();", + " s0 = mash(' ');", + " s1 = mash(' ');", + " s2 = mash(' ');", + " s0 -= mash(seed);", + " if (s0 < 0) {", + " s0 += 1;", + " }", + " s1 -= mash(seed);", + " if (s1 < 0) {", + " s1 += 1;", + " }", + " s2 -= mash(seed);", + " if (s2 < 0) {", + " s2 += 1;", + " }", + " return function () {", + " const t = 2091639 * s0 + c * 2.3283064365386963e-10; // 2^-32", + " s0 = s1;", + " s1 = s2;", + " return s2 = t - (c = t | 0);", + " };", + "}", + "", + "function masher() {", + " let n = 0xefc8249d;", + " return function (data) {", + " data = data.toString();", + " for (let i = 0; i < data.length; i++) {", + " n += data.charCodeAt(i);", + " let h = 0.02519603282416938 * n;", + " n = h >>> 0;", + " h -= n;", + " h *= n;", + " n = h >>> 0;", + " h -= n;", + " n += h * 0x100000000; // 2^32", + " }", + " return (n >>> 0) * 2.3283064365386963e-10; // 2^-32", + " };", + "}", + "", + "/** Deterministic simplex noise generator suitable for 2D, 3D and 4D spaces. */", + "class SimplexNoise {", + " /**", + " * Creates a new `SimplexNoise` instance.", + " * This involves some setup. You can save a few cpu cycles by reusing the same instance.", + " * @param {(() => number)|string|number} randomOrSeed A random number generator or a seed (string|number).", + " * Defaults to Math.random (random irreproducible initialization).", + " */", + " constructor(randomOrSeed) {", + " if (randomOrSeed === void 0) { randomOrSeed = Math.random; }", + " const random = typeof randomOrSeed == 'function' ? randomOrSeed : alea(randomOrSeed);", + " this.p = buildPermutationTable(random);", + " this.perm = new Uint8Array(512);", + " this.permMod12 = new Uint8Array(512);", + " for (let i = 0; i < 512; i++) {", + " this.perm[i] = this.p[i & 255];", + " this.permMod12[i] = this.perm[i] % 12;", + " }", + " }", + "", + " /**", + " * Samples the noise field in 2 dimensions", + " * @param {number} x", + " * @param {number} y", + " * @returns a number in the interval [-1, 1]", + " */", + " noise2D(x, y) {", + " const permMod12 = this.permMod12;", + " const perm = this.perm;", + " let n0 = 0; // Noise contributions from the three corners", + " let n1 = 0;", + " let n2 = 0;", + " // Skew the input space to determine which simplex cell we're in", + " const s = (x + y) * F2; // Hairy factor for 2D", + " const i = Math.floor(x + s);", + " const j = Math.floor(y + s);", + " const t = (i + j) * G2;", + " const X0 = i - t; // Unskew the cell origin back to (x,y) space", + " const Y0 = j - t;", + " const x0 = x - X0; // The x,y distances from the cell origin", + " const y0 = y - Y0;", + " // For the 2D case, the simplex shape is an equilateral triangle.", + " // Determine which simplex we are in.", + " let i1, j1; // Offsets for second (middle) corner of simplex in (i,j) coords", + " if (x0 > y0) {", + " i1 = 1;", + " j1 = 0;", + " } // lower triangle, XY order: (0,0)->(1,0)->(1,1)", + " else {", + " i1 = 0;", + " j1 = 1;", + " } // upper triangle, YX order: (0,0)->(0,1)->(1,1)", + " // A step of (1,0) in (i,j) means a step of (1-c,-c) in (x,y), and", + " // a step of (0,1) in (i,j) means a step of (-c,1-c) in (x,y), where", + " // c = (3-sqrt(3))/6", + " const x1 = x0 - i1 + G2; // Offsets for middle corner in (x,y) unskewed coords", + " const y1 = y0 - j1 + G2;", + " const x2 = x0 - 1.0 + 2.0 * G2; // Offsets for last corner in (x,y) unskewed coords", + " const y2 = y0 - 1.0 + 2.0 * G2;", + " // Work out the hashed gradient indices of the three simplex corners", + " const ii = i & 255;", + " const jj = j & 255;", + " // Calculate the contribution from the three corners", + " let t0 = 0.5 - x0 * x0 - y0 * y0;", + " if (t0 >= 0) {", + " const gi0 = permMod12[ii + perm[jj]] * 3;", + " t0 *= t0;", + " n0 = t0 * t0 * (grad3[gi0] * x0 + grad3[gi0 + 1] * y0); // (x,y) of grad3 used for 2D gradient", + " }", + " let t1 = 0.5 - x1 * x1 - y1 * y1;", + " if (t1 >= 0) {", + " const gi1 = permMod12[ii + i1 + perm[jj + j1]] * 3;", + " t1 *= t1;", + " n1 = t1 * t1 * (grad3[gi1] * x1 + grad3[gi1 + 1] * y1);", + " }", + " let t2 = 0.5 - x2 * x2 - y2 * y2;", + " if (t2 >= 0) {", + " const gi2 = permMod12[ii + 1 + perm[jj + 1]] * 3;", + " t2 *= t2;", + " n2 = t2 * t2 * (grad3[gi2] * x2 + grad3[gi2 + 1] * y2);", + " }", + " // Add contributions from each corner to get the final noise value.", + " // The result is scaled to return values in the interval [-1,1].", + " return 70.0 * (n0 + n1 + n2);", + " }", + "", + " /**", + " * Samples the noise field in 3 dimensions", + " * @param {number} x", + " * @param {number} y", + " * @param {number} z", + " * @returns a number in the interval [-1, 1]", + " */", + " noise3D(x, y, z) {", + " const permMod12 = this.permMod12;", + " const perm = this.perm;", + " let n0, n1, n2, n3; // Noise contributions from the four corners", + " // Skew the input space to determine which simplex cell we're in", + " const s = (x + y + z) * F3; // Very nice and simple skew factor for 3D", + " const i = Math.floor(x + s);", + " const j = Math.floor(y + s);", + " const k = Math.floor(z + s);", + " const t = (i + j + k) * G3;", + " const X0 = i - t; // Unskew the cell origin back to (x,y,z) space", + " const Y0 = j - t;", + " const Z0 = k - t;", + " const x0 = x - X0; // The x,y,z distances from the cell origin", + " const y0 = y - Y0;", + " const z0 = z - Z0;", + " // For the 3D case, the simplex shape is a slightly irregular tetrahedron.", + " // Determine which simplex we are in.", + " let i1, j1, k1; // Offsets for second corner of simplex in (i,j,k) coords", + " let i2, j2, k2; // Offsets for third corner of simplex in (i,j,k) coords", + " if (x0 >= y0) {", + " if (y0 >= z0) {", + " i1 = 1;", + " j1 = 0;", + " k1 = 0;", + " i2 = 1;", + " j2 = 1;", + " k2 = 0;", + " } // X Y Z order", + " else if (x0 >= z0) {", + " i1 = 1;", + " j1 = 0;", + " k1 = 0;", + " i2 = 1;", + " j2 = 0;", + " k2 = 1;", + " } // X Z Y order", + " else {", + " i1 = 0;", + " j1 = 0;", + " k1 = 1;", + " i2 = 1;", + " j2 = 0;", + " k2 = 1;", + " } // Z X Y order", + " }", + " else { // x0 y0)", + " rankx++;", + " else", + " ranky++;", + " if (x0 > z0)", + " rankx++;", + " else", + " rankz++;", + " if (x0 > w0)", + " rankx++;", + " else", + " rankw++;", + " if (y0 > z0)", + " ranky++;", + " else", + " rankz++;", + " if (y0 > w0)", + " ranky++;", + " else", + " rankw++;", + " if (z0 > w0)", + " rankz++;", + " else", + " rankw++;", + " // simplex[c] is a 4-vector with the numbers 0, 1, 2 and 3 in some order.", + " // Many values of c will never occur, since e.g. x>y>z>w makes x= 3 ? 1 : 0;", + " const j1 = ranky >= 3 ? 1 : 0;", + " const k1 = rankz >= 3 ? 1 : 0;", + " const l1 = rankw >= 3 ? 1 : 0;", + " // The integer offsets for the third simplex corner", + " const i2 = rankx >= 2 ? 1 : 0;", + " const j2 = ranky >= 2 ? 1 : 0;", + " const k2 = rankz >= 2 ? 1 : 0;", + " const l2 = rankw >= 2 ? 1 : 0;", + " // The integer offsets for the fourth simplex corner", + " const i3 = rankx >= 1 ? 1 : 0;", + " const j3 = ranky >= 1 ? 1 : 0;", + " const k3 = rankz >= 1 ? 1 : 0;", + " const l3 = rankw >= 1 ? 1 : 0;", + " // The fifth corner has all coordinate offsets = 1, so no need to compute that.", + " const x1 = x0 - i1 + G4; // Offsets for second corner in (x,y,z,w) coords", + " const y1 = y0 - j1 + G4;", + " const z1 = z0 - k1 + G4;", + " const w1 = w0 - l1 + G4;", + " const x2 = x0 - i2 + 2.0 * G4; // Offsets for third corner in (x,y,z,w) coords", + " const y2 = y0 - j2 + 2.0 * G4;", + " const z2 = z0 - k2 + 2.0 * G4;", + " const w2 = w0 - l2 + 2.0 * G4;", + " const x3 = x0 - i3 + 3.0 * G4; // Offsets for fourth corner in (x,y,z,w) coords", + " const y3 = y0 - j3 + 3.0 * G4;", + " const z3 = z0 - k3 + 3.0 * G4;", + " const w3 = w0 - l3 + 3.0 * G4;", + " const x4 = x0 - 1.0 + 4.0 * G4; // Offsets for last corner in (x,y,z,w) coords", + " const y4 = y0 - 1.0 + 4.0 * G4;", + " const z4 = z0 - 1.0 + 4.0 * G4;", + " const w4 = w0 - 1.0 + 4.0 * G4;", + " // Work out the hashed gradient indices of the five simplex corners", + " const ii = i & 255;", + " const jj = j & 255;", + " const kk = k & 255;", + " const ll = l & 255;", + " // Calculate the contribution from the five corners", + " let t0 = 0.6 - x0 * x0 - y0 * y0 - z0 * z0 - w0 * w0;", + " if (t0 < 0)", + " n0 = 0.0;", + " else {", + " const gi0 = (perm[ii + perm[jj + perm[kk + perm[ll]]]] % 32) * 4;", + " t0 *= t0;", + " n0 = t0 * t0 * (grad4[gi0] * x0 + grad4[gi0 + 1] * y0 + grad4[gi0 + 2] * z0 + grad4[gi0 + 3] * w0);", + " }", + " let t1 = 0.6 - x1 * x1 - y1 * y1 - z1 * z1 - w1 * w1;", + " if (t1 < 0)", + " n1 = 0.0;", + " else {", + " const gi1 = (perm[ii + i1 + perm[jj + j1 + perm[kk + k1 + perm[ll + l1]]]] % 32) * 4;", + " t1 *= t1;", + " n1 = t1 * t1 * (grad4[gi1] * x1 + grad4[gi1 + 1] * y1 + grad4[gi1 + 2] * z1 + grad4[gi1 + 3] * w1);", + " }", + " let t2 = 0.6 - x2 * x2 - y2 * y2 - z2 * z2 - w2 * w2;", + " if (t2 < 0)", + " n2 = 0.0;", + " else {", + " const gi2 = (perm[ii + i2 + perm[jj + j2 + perm[kk + k2 + perm[ll + l2]]]] % 32) * 4;", + " t2 *= t2;", + " n2 = t2 * t2 * (grad4[gi2] * x2 + grad4[gi2 + 1] * y2 + grad4[gi2 + 2] * z2 + grad4[gi2 + 3] * w2);", + " }", + " let t3 = 0.6 - x3 * x3 - y3 * y3 - z3 * z3 - w3 * w3;", + " if (t3 < 0)", + " n3 = 0.0;", + " else {", + " const gi3 = (perm[ii + i3 + perm[jj + j3 + perm[kk + k3 + perm[ll + l3]]]] % 32) * 4;", + " t3 *= t3;", + " n3 = t3 * t3 * (grad4[gi3] * x3 + grad4[gi3 + 1] * y3 + grad4[gi3 + 2] * z3 + grad4[gi3 + 3] * w3);", + " }", + " let t4 = 0.6 - x4 * x4 - y4 * y4 - z4 * z4 - w4 * w4;", + " if (t4 < 0)", + " n4 = 0.0;", + " else {", + " const gi4 = (perm[ii + 1 + perm[jj + 1 + perm[kk + 1 + perm[ll + 1]]]] % 32) * 4;", + " t4 *= t4;", + " n4 = t4 * t4 * (grad4[gi4] * x4 + grad4[gi4 + 1] * y4 + grad4[gi4 + 2] * z4 + grad4[gi4 + 3] * w4);", + " }", + " // Sum up and scale the result to cover the range [-1,1]", + " return 27.0 * (n0 + n1 + n2 + n3 + n4);", + " };", + "}", + "", + "gdjs._shakeObjectExtension = {", + " noiseManager: new NoiseManager(),", + "};", + "" + ], + "parameterObjects": "", + "useStrict": true, + "eventsSheetExpanded": true + } + ], + "parameters": [], + "objectGroups": [] + }, + { + "description": "Generate a number from 2 dimensional simplex noise.", + "fullName": "2D noise", + "functionType": "Expression", + "name": "Noise2d", + "private": true, + "sentence": "", + "events": [ + { + "type": "BuiltinCommonInstructions::JsCode", + "inlineCode": [ + "const name = eventsFunctionContext.getArgument(\"Name\");\r", + "const x = eventsFunctionContext.getArgument(\"X\");\r", + "const y = eventsFunctionContext.getArgument(\"Y\");\r", + "\r", + "eventsFunctionContext.returnValue = gdjs._shakeObjectExtension.noiseManager.getGenerator(name).noise(x, y);" + ], + "parameterObjects": "", + "useStrict": true, + "eventsSheetExpanded": true + } + ], + "expressionType": { + "type": "expression" + }, + "parameters": [ + { + "description": "Generator name", + "name": "Name", + "type": "string" + }, + { + "description": "X coordinate", + "name": "X", + "type": "expression" + }, + { + "description": "Y coordinate", + "name": "Y", + "type": "expression" + } + ], + "objectGroups": [] + }, + { + "description": "Generate a number from 3 dimensional simplex noise.", + "fullName": "3D noise", + "functionType": "Expression", + "name": "Noise3d", + "private": true, + "sentence": "", + "events": [ + { + "type": "BuiltinCommonInstructions::JsCode", + "inlineCode": [ + "const name = eventsFunctionContext.getArgument(\"Name\");\r", + "const x = eventsFunctionContext.getArgument(\"X\");\r", + "const y = eventsFunctionContext.getArgument(\"Y\");\r", + "const z = eventsFunctionContext.getArgument(\"Z\");\r", + "\r", + "eventsFunctionContext.returnValue = gdjs._shakeObjectExtension.noiseManager.getGenerator(name).noise(x, y, z);" + ], + "parameterObjects": "", + "useStrict": true, + "eventsSheetExpanded": true + } + ], + "expressionType": { + "type": "expression" + }, + "parameters": [ + { + "description": "Generator name", + "name": "Name", + "type": "string" + }, + { + "description": "X coordinate", + "name": "X", + "type": "expression" + }, + { + "description": "Y coordinate", + "name": "Y", + "type": "expression" + }, + { + "description": "Z coordinate", + "name": "Z", + "type": "expression" + } + ], + "objectGroups": [] + }, + { + "description": "Generate a number from 4 dimensional simplex noise.", + "fullName": "4D noise", + "functionType": "Expression", + "name": "Noise4d", + "private": true, + "sentence": "", + "events": [ + { + "type": "BuiltinCommonInstructions::JsCode", + "inlineCode": [ + "const name = eventsFunctionContext.getArgument(\"Name\");\r", + "const x = eventsFunctionContext.getArgument(\"X\");\r", + "const y = eventsFunctionContext.getArgument(\"Y\");\r", + "const z = eventsFunctionContext.getArgument(\"Z\");\r", + "const w = eventsFunctionContext.getArgument(\"W\");\r", + "\r", + "eventsFunctionContext.returnValue = gdjs._shakeObjectExtension.noiseManager.getGenerator(name).noise(x, y, z, w);" + ], + "parameterObjects": "", + "useStrict": true, + "eventsSheetExpanded": true + } + ], + "expressionType": { + "type": "expression" + }, + "parameters": [ + { + "description": "Generator name", + "name": "Name", + "type": "string" + }, + { + "description": "X coordinate", + "name": "X", + "type": "expression" + }, + { + "description": "Y coordinate", + "name": "Y", + "type": "expression" + }, + { + "description": "Z coordinate", + "name": "Z", + "type": "expression" + }, + { + "description": "W coordinate", + "name": "W", + "type": "expression" + } + ], + "objectGroups": [] + }, + { + "description": "Create a noise generator with default settings (frequency = 1, octaves = 1, persistence = 0.5, lacunarity = 2).", + "fullName": "Create a noise generator", + "functionType": "Action", + "name": "Create", + "private": true, + "sentence": "Create a noise generator named _PARAM1_", + "events": [ + { + "type": "BuiltinCommonInstructions::JsCode", + "inlineCode": [ + "const name = eventsFunctionContext.getArgument(\"Name\");\r", + "\r", + "gdjs._shakeObjectExtension.noiseManager.getGenerator(name);" + ], + "parameterObjects": "", + "useStrict": true, + "eventsSheetExpanded": false + } + ], + "parameters": [ + { + "description": "Generator name", + "name": "Name", + "type": "string" + } + ], + "objectGroups": [] + }, + { + "description": "Delete a noise generator and loose its settings.", + "fullName": "Delete a noise generator", + "functionType": "Action", + "name": "Delete", + "private": true, + "sentence": "Delete _PARAM1_ noise generator", + "events": [ + { + "type": "BuiltinCommonInstructions::JsCode", + "inlineCode": [ + "const name = eventsFunctionContext.getArgument(\"Name\");\r", + "\r", + "gdjs._shakeObjectExtension.noiseManager.deleteGenerator(name);" + ], + "parameterObjects": "", + "useStrict": true, + "eventsSheetExpanded": false + } + ], + "parameters": [ + { + "description": "Generator name", + "name": "Name", + "type": "string" + } + ], + "objectGroups": [] + }, + { + "description": "Delete all noise generators and loose their settings.", + "fullName": "Delete all noise generators", + "functionType": "Action", + "name": "DeleteAll", + "private": true, + "sentence": "Delete all noise generators", + "events": [ + { + "type": "BuiltinCommonInstructions::JsCode", + "inlineCode": "gdjs._shakeObjectExtension.noiseManager.deleteAllGenerators();", + "parameterObjects": "", + "useStrict": true, + "eventsSheetExpanded": false + } + ], + "parameters": [], + "objectGroups": [] + }, + { + "description": "The seed is a number used to generate the random noise. Setting the same seed will result in the same random noise generation. It's for example useful to generate the same world, by saving this seed value and reusing it later to generate again a world.", + "fullName": "Noise seed", + "functionType": "Action", + "name": "SetSeed", + "private": true, + "sentence": "Change the noise seed to _PARAM1_", + "events": [ + { + "type": "BuiltinCommonInstructions::JsCode", + "inlineCode": "gdjs._shakeObjectExtension.noiseManager.setSeed(eventsFunctionContext.getArgument(\"Seed\"));", + "parameterObjects": "", + "useStrict": true, + "eventsSheetExpanded": false + } + ], + "parameters": [ + { + "description": "Seed", + "longDescription": "15 digits numbers maximum", + "name": "Seed", + "type": "expression" + } + ], + "objectGroups": [] + }, + { + "description": "Change the looping period on X used for noise generation. The noise will wrap-around on X.", + "fullName": "Noise looping period on X", + "functionType": "Action", + "name": "SetLoopPeriodX", + "private": true, + "sentence": "Change the looping period on X of _PARAM2_: _PARAM1_", + "events": [ + { + "type": "BuiltinCommonInstructions::JsCode", + "inlineCode": [ + "const name = eventsFunctionContext.getArgument(\"Name\");\r", + "\r", + "gdjs._shakeObjectExtension.noiseManager.getGenerator(name).xLoopPeriod = eventsFunctionContext.getArgument(\"LoopPeriod\");" + ], + "parameterObjects": "", + "useStrict": true, + "eventsSheetExpanded": false + } + ], + "parameters": [ + { + "description": "Looping period on X", + "name": "LoopPeriod", + "type": "expression" + }, + { + "description": "Generator name", + "name": "Name", + "type": "string" + } + ], + "objectGroups": [] + }, + { + "description": "Change the looping period on Y used for noise generation. The noise will wrap-around on Y.", + "fullName": "Noise looping period on Y", + "functionType": "Action", + "name": "SetLoopPeriodY", + "private": true, + "sentence": "Change the looping period on Y of _PARAM2_: _PARAM1_", + "events": [ + { + "type": "BuiltinCommonInstructions::JsCode", + "inlineCode": [ + "const name = eventsFunctionContext.getArgument(\"Name\");\r", + "\r", + "gdjs._shakeObjectExtension.noiseManager.getGenerator(name).yLoopPeriod = eventsFunctionContext.getArgument(\"LoopPeriod\");" + ], + "parameterObjects": "", + "useStrict": true, + "eventsSheetExpanded": true + } + ], + "parameters": [ + { + "description": "Looping period on Y", + "name": "LoopPeriod", + "type": "expression" + }, + { + "description": "Generator name", + "name": "Name", + "type": "string" + } + ], + "objectGroups": [] + }, + { + "description": "Change the base frequency used for noise generation. A lower frequency will zoom in the noise.", + "fullName": "Noise base frequency", + "functionType": "Action", + "name": "SetFrequency", + "private": true, + "sentence": "Change the noise frequency of _PARAM2_: _PARAM1_", + "events": [ + { + "type": "BuiltinCommonInstructions::JsCode", + "inlineCode": [ + "const name = eventsFunctionContext.getArgument(\"Name\");\r", + "\r", + "gdjs._shakeObjectExtension.noiseManager.getGenerator(name).frequency = eventsFunctionContext.getArgument(\"Frequency\");" + ], + "parameterObjects": "", + "useStrict": true, + "eventsSheetExpanded": false + } + ], + "parameters": [ + { + "description": "Frequency", + "name": "Frequency", + "type": "expression" + }, + { + "description": "Generator name", + "name": "Name", + "type": "string" + } + ], + "objectGroups": [] + }, + { + "description": "Change the number of octaves used for noise generation. It can be seen as layers of noise with different zoom.", + "fullName": "Noise octaves", + "functionType": "Action", + "name": "SetOctaves", + "private": true, + "sentence": "Change the number of noise octaves of _PARAM2_: _PARAM1_", + "events": [ + { + "type": "BuiltinCommonInstructions::JsCode", + "inlineCode": [ + "const name = eventsFunctionContext.getArgument(\"Name\");\r", + "\r", + "gdjs._shakeObjectExtension.noiseManager.getGenerator(name).octaves = eventsFunctionContext.getArgument(\"Octaves\");" + ], + "parameterObjects": "", + "useStrict": true, + "eventsSheetExpanded": false + } + ], + "parameters": [ + { + "description": "Octaves", + "name": "Octaves", + "type": "expression" + }, + { + "description": "Generator name", + "name": "Name", + "type": "string" + } + ], + "objectGroups": [] + }, + { + "description": "Change the persistence used for noise generation. At its default value \"0.5\", it halves the noise amplitude at each octave.", + "fullName": "Noise persistence", + "functionType": "Action", + "name": "SetPersistence", + "private": true, + "sentence": "Change the noise persistence of _PARAM2_: _PARAM1_", + "events": [ + { + "type": "BuiltinCommonInstructions::JsCode", + "inlineCode": [ + "const name = eventsFunctionContext.getArgument(\"Name\");\r", + "\r", + "gdjs._shakeObjectExtension.noiseManager.getGenerator(name).persistence = eventsFunctionContext.getArgument(\"Persistence\");" + ], + "parameterObjects": "", + "useStrict": true, + "eventsSheetExpanded": false + } + ], + "parameters": [ + { + "description": "Persistence", + "name": "Persistence", + "type": "expression" + }, + { + "description": "Generator name", + "name": "Name", + "type": "string" + } + ], + "objectGroups": [] + }, + { + "description": "Change the lacunarity used for noise generation. At its default value \"2\", it doubles the frequency at each octave.", + "fullName": "Noise lacunarity", + "functionType": "Action", + "name": "SetLacunarity", + "private": true, + "sentence": "Change the noise lacunarity of _PARAM2_: _PARAM1_", + "events": [ + { + "type": "BuiltinCommonInstructions::JsCode", + "inlineCode": [ + "const name = eventsFunctionContext.getArgument(\"Name\");\r", + "\r", + "gdjs._shakeObjectExtension.noiseManager.getGenerator(name).lacunarity = eventsFunctionContext.getArgument(\"Lacunarity\");" + ], + "parameterObjects": "", + "useStrict": true, + "eventsSheetExpanded": false + } + ], + "parameters": [ + { + "description": "Lacunarity", + "name": "Lacunarity", + "type": "expression" + }, + { + "description": "Generator name", + "name": "Name", + "type": "string" + } + ], + "objectGroups": [] + }, + { + "description": "The seed used for noise generation.", + "fullName": "Noise seed", + "functionType": "Expression", + "name": "Seed", + "private": true, + "sentence": "", + "events": [ + { + "type": "BuiltinCommonInstructions::JsCode", + "inlineCode": "eventsFunctionContext.returnValue = gdjs._shakeObjectExtension.noiseManager.seed;", + "parameterObjects": "", + "useStrict": true, + "eventsSheetExpanded": false + } + ], + "expressionType": { + "type": "expression" + }, + "parameters": [], + "objectGroups": [] + }, + { + "description": "The base frequency used for noise generation.", + "fullName": "Noise base frequency", + "functionType": "Expression", + "name": "Frequency", + "private": true, + "sentence": "", + "events": [ + { + "type": "BuiltinCommonInstructions::JsCode", + "inlineCode": [ + "const name = eventsFunctionContext.getArgument(\"Name\");\r", + "\r", + "eventsFunctionContext.returnValue = gdjs._shakeObjectExtension.noiseManager.getGenerator(name).frequency;" + ], + "parameterObjects": "", + "useStrict": true, + "eventsSheetExpanded": false + } + ], + "expressionType": { + "type": "expression" + }, + "parameters": [ + { + "description": "Generator name", + "name": "Name", + "type": "string" + } + ], + "objectGroups": [] + }, + { + "description": "The number of octaves used for noise generation.", + "fullName": "Noise octaves number", + "functionType": "Expression", + "name": "Octaves", + "private": true, + "sentence": "", + "events": [ + { + "type": "BuiltinCommonInstructions::JsCode", + "inlineCode": [ + "const name = eventsFunctionContext.getArgument(\"Name\");\r", + "\r", + "eventsFunctionContext.returnValue = gdjs._shakeObjectExtension.noiseManager.getGenerator(name).octaves;" + ], + "parameterObjects": "", + "useStrict": true, + "eventsSheetExpanded": false + } + ], + "expressionType": { + "type": "expression" + }, + "parameters": [ + { + "description": "Generator name", + "name": "Name", + "type": "string" + } + ], + "objectGroups": [] + }, + { + "description": "The persistence used for noise generation.", + "fullName": "Noise persistence", + "functionType": "Expression", + "name": "Persistence", + "private": true, + "sentence": "", + "events": [ + { + "type": "BuiltinCommonInstructions::JsCode", + "inlineCode": [ + "const name = eventsFunctionContext.getArgument(\"Name\");\r", + "\r", + "eventsFunctionContext.returnValue = gdjs._shakeObjectExtension.noiseManager.getGenerator(name).persistence;" + ], + "parameterObjects": "", + "useStrict": true, + "eventsSheetExpanded": false + } + ], + "expressionType": { + "type": "expression" + }, + "parameters": [ + { + "description": "Generator name", + "name": "Name", + "type": "string" + } + ], + "objectGroups": [] + }, + { + "description": "The lacunarity used for noise generation.", + "fullName": "Noise lacunarity", + "functionType": "Expression", + "name": "Lacunarity", + "private": true, + "sentence": "", + "events": [ + { + "type": "BuiltinCommonInstructions::JsCode", + "inlineCode": [ + "const name = eventsFunctionContext.getArgument(\"Name\");\r", + "\r", + "eventsFunctionContext.returnValue = gdjs._shakeObjectExtension.noiseManager.getGenerator(name).lacunarity;" + ], + "parameterObjects": "", + "useStrict": true, + "eventsSheetExpanded": false + } + ], + "expressionType": { + "type": "expression" + }, + "parameters": [ + { + "description": "Generator name", + "name": "Name", + "type": "string" + } + ], + "objectGroups": [] + } + ], + "eventsFunctionsFolderStructure": { + "folderName": "__ROOT", + "children": [ + { + "functionName": "onFirstSceneLoaded" + }, + { + "functionName": "DefineHelperClasses" + }, + { + "functionName": "Noise2d" + }, + { + "functionName": "Noise3d" + }, + { + "functionName": "Noise4d" + }, + { + "functionName": "Create" + }, + { + "functionName": "Delete" + }, + { + "functionName": "DeleteAll" + }, + { + "functionName": "SetSeed" + }, + { + "functionName": "SetLoopPeriodX" + }, + { + "functionName": "SetLoopPeriodY" + }, + { + "functionName": "SetFrequency" + }, + { + "functionName": "SetOctaves" + }, + { + "functionName": "SetPersistence" + }, + { + "functionName": "SetLacunarity" + }, + { + "functionName": "Seed" + }, + { + "functionName": "Frequency" + }, + { + "functionName": "Octaves" + }, + { + "functionName": "Persistence" + }, + { + "functionName": "Lacunarity" + } + ] + }, + "eventsBasedBehaviors": [ + { + "description": "Shake 3D objects with translation and rotation.", + "fullName": "3D shake", + "helpPath": "", + "iconUrl": "", + "name": "ShakeModel3D", + "objectType": "", + "previewIconUrl": "", + "quickCustomizationVisibility": "hidden", + "eventsFunctions": [ + { + "fullName": "", + "functionType": "Action", + "name": "onCreated", + "sentence": "", + "events": [ + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [ + { + "type": { + "value": "BooleanVariable" + }, + "parameters": [ + "IsStartingAtCreation", + "True", + "" + ] + } + ], + "actions": [ + { + "type": { + "value": "ShakeObject3D::DefineHelperClasses" + }, + "parameters": [ + "", + "" + ] + }, + { + "type": { + "value": "ShakeObject3D::ShakeModel3D::StartShaking" + }, + "parameters": [ + "Object", + "Behavior", + "0", + "" + ] + } + ] + } + ], + "parameters": [ + { + "description": "Object", + "name": "Object", + "type": "object" + }, + { + "description": "Behavior", + "name": "Behavior", + "supplementaryInformation": "ShakeObject3D::ShakeModel3D", + "type": "behavior" + } + ], + "objectGroups": [] + }, + { + "fullName": "", + "functionType": "Action", + "name": "doStepPostEvents", + "sentence": "", + "events": [ + { + "type": "BuiltinCommonInstructions::Comment", + "color": { + "b": 109, + "g": 230, + "r": 255, + "textB": 0, + "textG": 0, + "textR": 0 + }, + "comment": "Step time counters." + }, + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [], + "actions": [ + { + "type": { + "value": "SetNumberVariable" + }, + "parameters": [ + "Time", + "+", + "TimeDelta()" + ] + } + ] + }, + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [ + { + "type": { + "value": "ShakeObject3D::ShakeModel3D::IsShaking" + }, + "parameters": [ + "Object", + "Behavior", + "" + ] + } + ], + "actions": [ + { + "type": { + "value": "SetNumberVariable" + }, + "parameters": [ + "NoiseTime", + "+", + "TimeDelta() * Frequency" + ] + }, + { + "type": { + "value": "ShakeObject3D::ShakeModel3D::SetSharedPropertyEasingFactor" + }, + "parameters": [ + "Object", + "Behavior", + "=", + "min(Object.Behavior::StartEasingFactor(), Object.Behavior::StopEasingFactor())" + ] + } + ], + "events": [ + { + "type": "BuiltinCommonInstructions::Comment", + "color": { + "b": 109, + "g": 230, + "r": 255, + "textB": 0, + "textG": 0, + "textR": 0 + }, + "comment": "Shake the object.\nSave the object displacement to revert it in onScenePostEvents." + }, + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [ + { + "type": { + "value": "NumberVariable" + }, + "parameters": [ + "TranslationAmplitudeX", + "!=", + "0" + ] + } + ], + "actions": [ + { + "type": { + "value": "SetNumberVariable" + }, + "parameters": [ + "DeltaX", + "=", + "ShakeObject3D::Noise2d(\"\", NoiseTime, 1000) * TranslationAmplitudeX * EasingFactor" + ] + }, + { + "type": { + "value": "SetX" + }, + "parameters": [ + "Object", + "+", + "DeltaX" + ] + } + ] + }, + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [ + { + "type": { + "value": "NumberVariable" + }, + "parameters": [ + "TranslationAmplitudeY", + "!=", + "0" + ] + } + ], + "actions": [ + { + "type": { + "value": "SetNumberVariable" + }, + "parameters": [ + "DeltaY", + "=", + "ShakeObject3D::Noise2d(\"\", NoiseTime, 2000) * TranslationAmplitudeY * EasingFactor" + ] + }, + { + "type": { + "value": "SetY" + }, + "parameters": [ + "Object", + "+", + "DeltaY" + ] + } + ] + }, + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [ + { + "type": { + "value": "NumberVariable" + }, + "parameters": [ + "TranslationAmplitudeZ", + "!=", + "0" + ] + } + ], + "actions": [ + { + "type": { + "value": "SetNumberVariable" + }, + "parameters": [ + "DeltaZ", + "=", + "ShakeObject3D::Noise2d(\"\", NoiseTime, 3000) * TranslationAmplitudeZ * EasingFactor" + ] + }, + { + "type": { + "value": "Scene3D::Base3DBehavior::SetZ" + }, + "parameters": [ + "Object", + "Object3D", + "+", + "DeltaZ" + ] + } + ] + }, + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [ + { + "type": { + "value": "NumberVariable" + }, + "parameters": [ + "RotationAmplitudeX", + "!=", + "0" + ] + } + ], + "actions": [ + { + "type": { + "value": "SetNumberVariable" + }, + "parameters": [ + "DeltaAngleX", + "=", + "ShakeObject3D::Noise2d(\"\", NoiseTime, 4000) * RotationAmplitudeX * EasingFactor" + ] + }, + { + "type": { + "value": "Scene3D::Base3DBehavior::SetRotationX" + }, + "parameters": [ + "Object", + "Object3D", + "+", + "DeltaAngleX" + ] + } + ] + }, + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [ + { + "type": { + "value": "NumberVariable" + }, + "parameters": [ + "RotationAmplitudeY", + "!=", + "0" + ] + } + ], + "actions": [ + { + "type": { + "value": "SetNumberVariable" + }, + "parameters": [ + "DeltaAngleY", + "=", + "ShakeObject3D::Noise2d(\"\", NoiseTime, 5000) * RotationAmplitudeY * EasingFactor" + ] + }, + { + "type": { + "value": "Scene3D::Base3DBehavior::SetRotationY" + }, + "parameters": [ + "Object", + "Object3D", + "+", + "DeltaAngleY" + ] + } + ] + }, + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [ + { + "type": { + "value": "NumberVariable" + }, + "parameters": [ + "RotationAmplitudeZ", + "!=", + "0" + ] + } + ], + "actions": [ + { + "type": { + "value": "SetNumberVariable" + }, + "parameters": [ + "DeltaAngleZ", + "=", + "ShakeObject3D::Noise2d(\"\", NoiseTime, 6000) * RotationAmplitudeZ * EasingFactor" + ] + }, + { + "type": { + "value": "SetAngle" + }, + "parameters": [ + "Object", + "+", + "DeltaAngleZ" + ] + } + ] + } + ] + } + ], + "parameters": [ + { + "description": "Object", + "name": "Object", + "type": "object" + }, + { + "description": "Behavior", + "name": "Behavior", + "supplementaryInformation": "ShakeObject3D::ShakeModel3D", + "type": "behavior" + } + ], + "objectGroups": [] + }, + { + "fullName": "", + "functionType": "Action", + "name": "doStepPreEvents", + "sentence": "", + "events": [ + { + "type": "BuiltinCommonInstructions::Comment", + "color": { + "b": 109, + "g": 230, + "r": 255, + "textB": 0, + "textG": 0, + "textR": 0 + }, + "comment": "Revert the shaking." + }, + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [ + { + "type": { + "value": "ShakeObject3D::ShakeModel3D::IsShaking" + }, + "parameters": [ + "Object", + "Behavior", + "" + ] + } + ], + "actions": [], + "events": [ + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [ + { + "type": { + "value": "NumberVariable" + }, + "parameters": [ + "TranslationAmplitudeX", + "!=", + "0" + ] + } + ], + "actions": [ + { + "type": { + "value": "SetX" + }, + "parameters": [ + "Object", + "-", + "DeltaX" + ] + } + ] + }, + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [ + { + "type": { + "value": "NumberVariable" + }, + "parameters": [ + "TranslationAmplitudeY", + "!=", + "0" + ] + } + ], + "actions": [ + { + "type": { + "value": "SetY" + }, + "parameters": [ + "Object", + "-", + "DeltaY" + ] + } + ] + }, + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [ + { + "type": { + "value": "NumberVariable" + }, + "parameters": [ + "TranslationAmplitudeZ", + "!=", + "0" + ] + } + ], + "actions": [ + { + "type": { + "value": "Scene3D::Base3DBehavior::SetZ" + }, + "parameters": [ + "Object", + "Object3D", + "-", + "DeltaZ" + ] + } + ] + }, + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [ + { + "type": { + "value": "NumberVariable" + }, + "parameters": [ + "RotationAmplitudeX", + "!=", + "0" + ] + } + ], + "actions": [ + { + "type": { + "value": "Scene3D::Base3DBehavior::SetRotationX" + }, + "parameters": [ + "Object", + "Object3D", + "-", + "DeltaAngleX" + ] + } + ] + }, + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [ + { + "type": { + "value": "NumberVariable" + }, + "parameters": [ + "RotationAmplitudeY", + "!=", + "0" + ] + } + ], + "actions": [ + { + "type": { + "value": "Scene3D::Base3DBehavior::SetRotationY" + }, + "parameters": [ + "Object", + "Object3D", + "-", + "DeltaAngleY" + ] + } + ] + }, + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [ + { + "type": { + "value": "NumberVariable" + }, + "parameters": [ + "RotationAmplitudeZ", + "!=", + "0" + ] + } + ], + "actions": [ + { + "type": { + "value": "SetAngle" + }, + "parameters": [ + "Object", + "-", + "DeltaAngleZ" + ] + } + ] + } + ] + } + ], + "parameters": [ + { + "description": "Object", + "name": "Object", + "type": "object" + }, + { + "description": "Behavior", + "name": "Behavior", + "supplementaryInformation": "ShakeObject3D::ShakeModel3D", + "type": "behavior" + } + ], + "objectGroups": [] + }, + { + "description": "Shake the object with a linear easing at the start and the end.", + "fullName": "Shake", + "functionType": "Action", + "name": "Shake", + "sentence": "Shake _PARAM0_ for _PARAM2_ seconds with _PARAM3_ seconds of easing to start and _PARAM4_ seconds to stop", + "events": [ + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [], + "actions": [ + { + "type": { + "value": "SetNumberVariable" + }, + "parameters": [ + "Time", + "=", + "0" + ] + }, + { + "type": { + "value": "SetNumberVariable" + }, + "parameters": [ + "NoiseTime", + "=", + "TimeFromStart() * Frequency" + ] + }, + { + "type": { + "value": "SetNumberVariable" + }, + "parameters": [ + "Duration", + "=", + "NewDuration" + ] + }, + { + "type": { + "value": "SetNumberVariable" + }, + "parameters": [ + "StartEasingDuration", + "=", + "StartEaseDuration" + ] + }, + { + "type": { + "value": "SetNumberVariable" + }, + "parameters": [ + "StopEasingDuration", + "=", + "StopEaseDuration" + ] + } + ] + }, + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [ + { + "type": { + "value": "NumberVariable" + }, + "parameters": [ + "NewDuration", + "<", + "StartEaseDuration + StopEaseDuration" + ] + } + ], + "actions": [ + { + "type": { + "value": "SetNumberVariable" + }, + "parameters": [ + "StartEasingDuration", + "=", + "StartEaseDuration * NewDuration / (StartEaseDuration + StopEaseDuration)" + ] + }, + { + "type": { + "value": "SetNumberVariable" + }, + "parameters": [ + "StopEasingDuration", + "=", + "StopEaseDuration * NewDuration / (StartEaseDuration + StopEaseDuration)" + ] + } + ] + } + ], + "parameters": [ + { + "description": "Object", + "name": "Object", + "type": "object" + }, + { + "description": "Behavior", + "name": "Behavior", + "supplementaryInformation": "ShakeObject3D::ShakeModel3D", + "type": "behavior" + }, + { + "description": "Duration (in seconds)", + "name": "NewDuration", + "type": "expression" + }, + { + "description": "Ease duration to start (in seconds)", + "name": "StartEaseDuration", + "type": "expression" + }, + { + "description": "Ease duration to stop (in seconds)", + "name": "StopEaseDuration", + "type": "expression" + } + ], + "objectGroups": [] + }, + { + "description": "Shake the object with a linear easing at the start and keep shaking until the stop action is used.", + "fullName": "Start shaking", + "functionType": "Action", + "name": "StartShaking", + "sentence": "Start shaking _PARAM0_ with _PARAM2_ seconds of easing", + "events": [ + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [], + "actions": [ + { + "type": { + "value": "SetNumberVariable" + }, + "parameters": [ + "Time", + "=", + "0" + ] + }, + { + "type": { + "value": "SetNumberVariable" + }, + "parameters": [ + "NoiseTime", + "=", + "TimeFromStart() * Frequency" + ] + }, + { + "type": { + "value": "SetNumberVariable" + }, + "parameters": [ + "Duration", + "=", + "1234567890" + ] + }, + { + "type": { + "value": "SetNumberVariable" + }, + "parameters": [ + "StartEasingDuration", + "=", + "EaseDuration" + ] + } + ] + } + ], + "parameters": [ + { + "description": "Object", + "name": "Object", + "type": "object" + }, + { + "description": "Behavior", + "name": "Behavior", + "supplementaryInformation": "ShakeObject3D::ShakeModel3D", + "type": "behavior" + }, + { + "description": "Ease duration (in seconds)", + "name": "EaseDuration", + "type": "expression" + } + ], + "objectGroups": [] + }, + { + "description": "Stop shaking the object with a linear easing.", + "fullName": "Stop shaking", + "functionType": "Action", + "name": "StopShaking", + "sentence": "Stop shaking _PARAM0_ with _PARAM2_ seconds of easing", + "events": [ + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [ + { + "type": { + "value": "ShakeObject3D::ShakeModel3D::IsShaking" + }, + "parameters": [ + "Object", + "Behavior", + "" + ] + } + ], + "actions": [ + { + "type": { + "value": "SetNumberVariable" + }, + "parameters": [ + "Time", + "=", + "0" + ] + }, + { + "type": { + "value": "SetNumberVariable" + }, + "parameters": [ + "Duration", + "=", + "EaseDuration" + ] + }, + { + "type": { + "value": "SetNumberVariable" + }, + "parameters": [ + "StopEasingDuration", + "=", + "EaseDuration" + ] + } + ] + } + ], + "parameters": [ + { + "description": "Object", + "name": "Object", + "type": "object" + }, + { + "description": "Behavior", + "name": "Behavior", + "supplementaryInformation": "ShakeObject3D::ShakeModel3D", + "type": "behavior" + }, + { + "description": "Ease duration (in seconds)", + "name": "EaseDuration", + "type": "expression" + } + ], + "objectGroups": [] + }, + { + "description": "Check if the object is shaking.", + "fullName": "Is shaking", + "functionType": "Condition", + "name": "IsShaking", + "sentence": "_PARAM0_ is shaking", + "events": [ + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [ + { + "type": { + "value": "NumberVariable" + }, + "parameters": [ + "Time", + "<", + "Duration" + ] + }, + { + "type": { + "value": "NumberVariable" + }, + "parameters": [ + "Time", + ">", + "0" + ] + } + ], + "actions": [ + { + "type": { + "value": "SetReturnBoolean" + }, + "parameters": [ + "True" + ] + } + ] + } + ], + "parameters": [ + { + "description": "Object", + "name": "Object", + "type": "object" + }, + { + "description": "Behavior", + "name": "Behavior", + "supplementaryInformation": "ShakeObject3D::ShakeModel3D", + "type": "behavior" + } + ], + "objectGroups": [] + }, + { + "description": "Check if the object is stopping to shake.", + "fullName": "Is stopping to shake", + "functionType": "Condition", + "name": "IsStopping", + "sentence": "_PARAM0_ is stopping to shake", + "events": [ + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [ + { + "type": { + "value": "ShakeObject3D::ShakeModel3D::IsShaking" + }, + "parameters": [ + "Object", + "Behavior", + "" + ] + }, + { + "type": { + "value": "ShakeObject3D::ShakeModel3D::StopEasingFactor" + }, + "parameters": [ + "Object", + "Behavior", + "<", + "1", + "" + ] + } + ], + "actions": [ + { + "type": { + "value": "SetReturnBoolean" + }, + "parameters": [ + "True" + ] + } + ] + } + ], + "parameters": [ + { + "description": "Object", + "name": "Object", + "type": "object" + }, + { + "description": "Behavior", + "name": "Behavior", + "supplementaryInformation": "ShakeObject3D::ShakeModel3D", + "type": "behavior" + } + ], + "objectGroups": [] + }, + { + "description": "the shaking frequency of the object.", + "fullName": "Shaking frequency", + "functionType": "ExpressionAndCondition", + "group": "ShakeObject3D configuration", + "name": "Frequency", + "sentence": "the shaking frequency", + "events": [ + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [], + "actions": [ + { + "type": { + "value": "SetReturnNumber" + }, + "parameters": [ + "Frequency" + ] + } + ] + } + ], + "expressionType": { + "type": "expression" + }, + "parameters": [ + { + "description": "Object", + "name": "Object", + "type": "object" + }, + { + "description": "Behavior", + "name": "Behavior", + "supplementaryInformation": "ShakeObject3D::ShakeModel3D", + "type": "behavior" + } + ], + "objectGroups": [] + }, + { + "fullName": "", + "functionType": "ActionWithOperator", + "getterName": "Frequency", + "group": "ShakeObject3D configuration", + "name": "SetFrequency", + "sentence": "", + "events": [ + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [], + "actions": [ + { + "type": { + "value": "SetNumberVariable" + }, + "parameters": [ + "Frequency", + "=", + "Value" + ] + } + ] + } + ], + "parameters": [ + { + "description": "Object", + "name": "Object", + "type": "object" + }, + { + "description": "Behavior", + "name": "Behavior", + "supplementaryInformation": "ShakeObject3D::ShakeModel3D", + "type": "behavior" + } + ], + "objectGroups": [] + }, + { + "description": "Return the easing factor according to start properties.", + "fullName": "Start easing factor", + "functionType": "Expression", + "name": "StartEasingFactor", + "private": true, + "sentence": "", + "events": [ + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [ + { + "type": { + "value": "NumberVariable" + }, + "parameters": [ + "StartEasingDuration", + "<=", + "0" + ] + } + ], + "actions": [ + { + "type": { + "value": "SetReturnNumber" + }, + "parameters": [ + "1" + ] + } + ] + }, + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [ + { + "type": { + "value": "NumberVariable" + }, + "parameters": [ + "StartEasingDuration", + ">", + "0" + ] + } + ], + "actions": [ + { + "type": { + "value": "SetReturnNumber" + }, + "parameters": [ + "clamp(Time / StartEasingDuration, 0, 1)" + ] + } + ] + } + ], + "expressionType": { + "type": "expression" + }, + "parameters": [ + { + "description": "Object", + "name": "Object", + "type": "object" + }, + { + "description": "Behavior", + "name": "Behavior", + "supplementaryInformation": "ShakeObject3D::ShakeModel3D", + "type": "behavior" + } + ], + "objectGroups": [] + }, + { + "description": "Return the easing factor according to stop properties.", + "fullName": "Stop easing factor", + "functionType": "ExpressionAndCondition", + "name": "StopEasingFactor", + "private": true, + "sentence": "stop easing factor", + "events": [ + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [ + { + "type": { + "value": "NumberVariable" + }, + "parameters": [ + "StopEasingDuration", + "<=", + "0" + ] + } + ], + "actions": [], + "events": [ + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [ + { + "type": { + "value": "NumberVariable" + }, + "parameters": [ + "Time", + "<", + "Duration" + ] + } + ], + "actions": [ + { + "type": { + "value": "SetReturnNumber" + }, + "parameters": [ + "1" + ] + } + ] + }, + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [ + { + "type": { + "value": "NumberVariable" + }, + "parameters": [ + "Time", + ">=", + "Duration" + ] + } + ], + "actions": [ + { + "type": { + "value": "SetReturnNumber" + }, + "parameters": [ + "0" + ] + } + ] + } + ] + }, + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [ + { + "type": { + "value": "NumberVariable" + }, + "parameters": [ + "StopEasingDuration", + ">", + "0" + ] + } + ], + "actions": [ + { + "type": { + "value": "SetReturnNumber" + }, + "parameters": [ + "clamp((Duration - Time) / StopEasingDuration, 0, 1)" + ] + } + ] + } + ], + "expressionType": { + "type": "expression" + }, + "parameters": [ + { + "description": "Object", + "name": "Object", + "type": "object" + }, + { + "description": "Behavior", + "name": "Behavior", + "supplementaryInformation": "ShakeObject3D::ShakeModel3D", + "type": "behavior" + } + ], + "objectGroups": [] + } + ], + "eventsFunctionsFolderStructure": { + "folderName": "__ROOT", + "children": [ + { + "functionName": "onCreated" + }, + { + "functionName": "doStepPostEvents" + }, + { + "functionName": "doStepPreEvents" + }, + { + "functionName": "Shake" + }, + { + "functionName": "StartShaking" + }, + { + "functionName": "StopShaking" + }, + { + "functionName": "IsShaking" + }, + { + "functionName": "IsStopping" + }, + { + "folderName": "ShakeObject3D configuration", + "children": [ + { + "functionName": "Frequency" + }, + { + "functionName": "SetFrequency" + } + ] + }, + { + "functionName": "StartEasingFactor" + }, + { + "functionName": "StopEasingFactor" + } + ] + }, + "propertyDescriptors": [ + { + "value": "0", + "type": "Number", + "unit": "Pixel", + "label": "Translation amplitude on X axis", + "name": "TranslationAmplitudeX" + }, + { + "value": "0", + "type": "Number", + "unit": "Pixel", + "label": "Translation amplitude on Y axis", + "name": "TranslationAmplitudeY" + }, + { + "value": "0", + "type": "Number", + "unit": "Pixel", + "label": "Translation amplitude on Z axis", + "name": "TranslationAmplitudeZ" + }, + { + "value": "5", + "type": "Number", + "unit": "DegreeAngle", + "label": "Rotation amplitude around X axis", + "name": "RotationAmplitudeX" + }, + { + "value": "5", + "type": "Number", + "unit": "DegreeAngle", + "label": "Rotation amplitude around Y axis", + "name": "RotationAmplitudeY" + }, + { + "value": "5", + "type": "Number", + "unit": "DegreeAngle", + "label": "Rotation amplitude around Z axis", + "name": "RotationAmplitudeZ" + }, + { + "value": "", + "type": "Number", + "label": "", + "hidden": true, + "name": "Time" + }, + { + "value": "", + "type": "Number", + "label": "", + "hidden": true, + "name": "Duration" + }, + { + "value": "", + "type": "Number", + "label": "", + "hidden": true, + "name": "StartEasingDuration" + }, + { + "value": "", + "type": "Number", + "label": "", + "hidden": true, + "name": "StopEasingDuration" + }, + { + "value": "", + "type": "Number", + "label": "Frequency", + "name": "Frequency" + }, + { + "value": "", + "type": "Number", + "label": "", + "hidden": true, + "name": "DeltaX" + }, + { + "value": "", + "type": "Number", + "label": "", + "hidden": true, + "name": "DeltaY" + }, + { + "value": "", + "type": "Number", + "label": "", + "hidden": true, + "name": "DeltaZ" + }, + { + "value": "", + "type": "Number", + "label": "", + "hidden": true, + "name": "DeltaAngleX" + }, + { + "value": "", + "type": "Number", + "label": "", + "hidden": true, + "name": "DeltaAngleY" + }, + { + "value": "", + "type": "Number", + "label": "", + "hidden": true, + "name": "DeltaAngleZ" + }, + { + "value": "", + "type": "Number", + "label": "", + "hidden": true, + "name": "NoiseTime" + }, + { + "value": "", + "type": "Behavior", + "label": "3D capability", + "extraInformation": [ + "Scene3D::Base3DBehavior" + ], + "choices": [], + "name": "Object3D" + }, + { + "value": "", + "type": "Boolean", + "label": "Start to shake at the object creation", + "name": "IsStartingAtCreation" + } + ], + "propertiesFolderStructure": { + "folderName": "__ROOT", + "children": [ + { + "propertyName": "TranslationAmplitudeX" + }, + { + "propertyName": "TranslationAmplitudeY" + }, + { + "propertyName": "TranslationAmplitudeZ" + }, + { + "propertyName": "RotationAmplitudeX" + }, + { + "propertyName": "RotationAmplitudeY" + }, + { + "propertyName": "RotationAmplitudeZ" + }, + { + "propertyName": "Time" + }, + { + "propertyName": "Duration" + }, + { + "propertyName": "StartEasingDuration" + }, + { + "propertyName": "StopEasingDuration" + }, + { + "propertyName": "Frequency" + }, + { + "propertyName": "DeltaX" + }, + { + "propertyName": "DeltaY" + }, + { + "propertyName": "DeltaZ" + }, + { + "propertyName": "DeltaAngleX" + }, + { + "propertyName": "DeltaAngleY" + }, + { + "propertyName": "DeltaAngleZ" + }, + { + "propertyName": "NoiseTime" + }, + { + "propertyName": "Object3D" + }, + { + "propertyName": "IsStartingAtCreation" + } + ] + }, + "sharedPropertyDescriptors": [ + { + "value": "", + "type": "Number", + "label": "", + "hidden": true, + "name": "EasingFactor" + } + ], + "sharedPropertiesFolderStructure": { + "folderName": "__ROOT", + "children": [ + { + "propertyName": "EasingFactor" + } + ] + } + } + ], + "eventsBasedObjects": [] + }, + { + "author": "westboy31, Tristan Rhodes (https://victrisgames.itch.io/)", + "category": "Camera", + "dimension": "2D", + "extensionNamespace": "", + "fullName": "Camera shake", + "gdevelopVersion": ">=5.5.222", + "helpPath": "", + "iconUrl": "data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iVVRGLTgiPz48IURPQ1RZUEUgc3ZnIFBVQkxJQyAiLS8vVzNDLy9EVEQgU1ZHIDEuMS8vRU4iICJodHRwOi8vd3d3LnczLm9yZy9HcmFwaGljcy9TVkcvMS4xL0RURC9zdmcxMS5kdGQiPjxzdmcgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIiB4bWxuczp4bGluaz0iaHR0cDovL3d3dy53My5vcmcvMTk5OS94bGluayIgdmVyc2lvbj0iMS4xIiBpZD0ibWRpLXZlY3Rvci1kaWZmZXJlbmNlLWFiIiB3aWR0aD0iMjQiIGhlaWdodD0iMjQiIHZpZXdCb3g9IjAgMCAyNCAyNCI+PHBhdGggZD0iTTMsMUMxLjg5LDEgMSwxLjg5IDEsM1Y1SDNWM0g1VjFIM003LDFWM0gxMFYxSDdNMTIsMVYzSDE0VjVIMTZWM0MxNiwxLjg5IDE1LjExLDEgMTQsMUgxMk0xLDdWMTBIM1Y3SDFNMTQsN0MxNCw3IDE0LDExLjY3IDE0LDE0QzExLjY3LDE0IDcsMTQgNywxNEM3LDE0IDcsMTggNywyMEM3LDIxLjExIDcuODksMjIgOSwyMkgyMEMyMS4xMSwyMiAyMiwyMS4xMSAyMiwyMFY5QzIyLDcuODkgMjEuMTEsNyAyMCw3QzE4LDcgMTQsNyAxNCw3TTE2LDlIMjBWMjBIOVYxNkgxNEMxNS4xMSwxNiAxNiwxNS4xMSAxNiwxNFY5TTEsMTJWMTRDMSwxNS4xMSAxLjg5LDE2IDMsMTZINVYxNEgzVjEySDFaIiAvPjwvc3ZnPg==", + "name": "CameraShake", + "previewIconUrl": "https://resources.gdevelop-app.com/assets/Icons/vector-difference-ab.svg", + "shortDescription": "Shake layer cameras.", + "version": "3.2.0", + "description": [ + "Shake layer cameras with translation, rotation and zoom.", + "", + "- Short shaking can be used to give impact (explosion, hit)", + "- Shaking can go indefinitely to set an ambiance (engine vibration, earthquake, pulsing)", + "- Low frequency shaking allows to simulate slow moving objects (ship rocking back and forth)", + "", + "Release notes:", + "- Version 3.0.0", + " - No adaptation of the game events is needed.", + " - It fixes an issue when used with scrolling, the amplitude will feel bigger in this case.", + " - The shaking relies on noise which could feel a bit different.", + " - This extension can no longer do impulses. For this, another extension \"Camera impulse\" can be used." + ], + "origin": { + "identifier": "CameraShake", + "name": "gdevelop-extension-store" + }, + "tags": [ + "shaking", + "camera", + "effect", + "screen", + "shake" + ], + "authorIds": [ + "gqDaZjCfevOOxBYkK6zlhtZnXCg1", + "IWykYNRvhCZBN3vEgKEbBPOR3Oc2", + "m4hBMBTUilft4s1V4FQQPakVDGx1" + ], + "dependencies": [], + "globalVariables": [], + "sceneVariables": [ + { + "folded": true, + "name": "DefaultFrequency", + "type": "number", + "value": 12 + }, + { + "folded": true, + "name": "DefaultAmplitudeX", + "type": "number", + "value": 4 + }, + { + "folded": true, + "name": "DefaultAmplitudeY", + "type": "number", + "value": 4 + }, + { + "folded": true, + "name": "DefaultAmplitudeAngle", + "type": "number", + "value": 0 + }, + { + "folded": true, + "name": "DefaultAmplitudeZoom", + "type": "number", + "value": 1 + }, + { + "folded": true, + "name": "Duration", + "type": "number", + "value": 0 + }, + { + "name": "Layers", + "type": "structure", + "children": [] + }, + { + "name": "Layer", + "type": "structure", + "children": [ + { + "name": "AmplitudeAngle", + "type": "number", + "value": 0 + }, + { + "name": "AmplitudeX", + "type": "number", + "value": 0 + }, + { + "name": "AmplitudeY", + "type": "number", + "value": 0 + }, + { + "folded": true, + "name": "AmplitudeZoom", + "type": "number", + "value": 0 + }, + { + "folded": true, + "name": "CameraDeltaAngle", + "type": "number", + "value": 0 + }, + { + "name": "CameraDeltaX", + "type": "number", + "value": 0 + }, + { + "name": "CameraDeltaY", + "type": "number", + "value": 0 + }, + { + "folded": true, + "name": "CameraDeltaZoom", + "type": "number", + "value": 0 + }, + { + "folded": true, + "name": "Frequency", + "type": "number", + "value": 0 + } + ] + }, + { + "folded": true, + "name": "LayerName", + "type": "string", + "value": "" + }, + { + "folded": true, + "name": "Time", + "type": "number", + "value": 0 + }, + { + "folded": true, + "name": "StartEaseDuration", + "type": "number", + "value": 0 + }, + { + "folded": true, + "name": "StopEaseDuration", + "type": "number", + "value": 0 + } + ], + "eventsFunctions": [ + { + "fullName": "", + "functionType": "Action", + "name": "onSceneLoaded", + "sentence": "", + "events": [ + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [], + "actions": [ + { + "type": { + "value": "CameraShake::SetLayerShakable" + }, + "parameters": [ + "", + "", + "\"\"", + "" + ] + } + ] + } + ], + "parameters": [], + "objectGroups": [] + }, + { + "fullName": "", + "functionType": "Action", + "name": "onScenePostEvents", + "sentence": "", + "events": [ + { + "type": "BuiltinCommonInstructions::Comment", + "color": { + "b": 109, + "g": 230, + "r": 255, + "textB": 0, + "textG": 0, + "textR": 0 + }, + "comment": "Step time counters." + }, + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [], + "actions": [ + { + "type": { + "value": "SetNumberVariable" + }, + "parameters": [ + "Time", + "+", + "TimeDelta()" + ] + } + ] + }, + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [ + { + "type": { + "value": "CameraShake::IsShaking" + }, + "parameters": [ + "", + "" + ] + } + ], + "actions": [], + "events": [ + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [], + "actions": [], + "events": [ + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [ + { + "type": { + "value": "NumberVariable" + }, + "parameters": [ + "Time", + "<", + "StartEaseDuration" + ] + }, + { + "type": { + "inverted": true, + "value": "NumberVariable" + }, + "parameters": [ + "Time", + ">", + "Duration - StopEaseDuration" + ] + } + ], + "actions": [ + { + "type": { + "value": "SetNumberVariable" + }, + "parameters": [ + "EaseFactor", + "=", + "clamp(Time / StartEaseDuration, 0, 1)" + ] + } + ] + }, + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [ + { + "type": { + "value": "NumberVariable" + }, + "parameters": [ + "Time", + ">", + "Duration - StopEaseDuration" + ] + } + ], + "actions": [ + { + "type": { + "value": "SetNumberVariable" + }, + "parameters": [ + "EaseFactor", + "=", + "clamp((Duration - Time) / StopEaseDuration, 0, 1)" + ] + } + ] + }, + { + "type": "BuiltinCommonInstructions::ForEachChildVariable", + "iterableVariableName": "Layers", + "valueIteratorVariableName": "Layer", + "keyIteratorVariableName": "LayerName", + "conditions": [ + { + "type": { + "value": "BooleanVariable" + }, + "parameters": [ + "Layer.Shakable", + "True", + "" + ] + } + ], + "actions": [], + "events": [ + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [], + "actions": [ + { + "type": { + "value": "SetStringVariable" + }, + "parameters": [ + "ActualLayerName", + "=", + "LayerName" + ] + } + ] + }, + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [ + { + "type": { + "value": "StringVariable" + }, + "parameters": [ + "LayerName", + "=", + "\"__BaseLayer\"" + ] + } + ], + "actions": [ + { + "type": { + "value": "SetStringVariable" + }, + "parameters": [ + "ActualLayerName", + "=", + "\"\"" + ] + } + ] + }, + { + "type": "BuiltinCommonInstructions::Comment", + "color": { + "b": 109, + "g": 230, + "r": 255, + "textB": 0, + "textG": 0, + "textR": 0 + }, + "comment": "Use user defined default values when there is no layer specific value set." + }, + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [], + "actions": [ + { + "type": { + "value": "CameraShake::SetFrequency" + }, + "parameters": [ + "", + "DefaultFrequency", + "\"\"", + "" + ] + }, + { + "type": { + "value": "SetNumberVariable" + }, + "parameters": [ + "AmplitudeX", + "=", + "DefaultAmplitudeX" + ] + }, + { + "type": { + "value": "SetNumberVariable" + }, + "parameters": [ + "AmplitudeY", + "=", + "DefaultAmplitudeY" + ] + }, + { + "type": { + "value": "SetNumberVariable" + }, + "parameters": [ + "AmplitudeAngle", + "=", + "DefaultAmplitudeAngle" + ] + }, + { + "type": { + "value": "SetNumberVariable" + }, + "parameters": [ + "AmplitudeZoom", + "=", + "DefaultAmplitudeZoom" + ] + }, + { + "type": { + "value": "SetNumberVariable" + }, + "parameters": [ + "Layers[LayerName].CameraDeltaX", + "=", + "0" + ] + }, + { + "type": { + "value": "SetNumberVariable" + }, + "parameters": [ + "Layers[LayerName].CameraDeltaY", + "=", + "0" + ] + }, + { + "type": { + "value": "SetNumberVariable" + }, + "parameters": [ + "Layers[LayerName].CameraDeltaAngle", + "=", + "0" + ] + }, + { + "type": { + "value": "SetNumberVariable" + }, + "parameters": [ + "Layers[LayerName].CameraDeltaZoom", + "=", + "0" + ] + } + ], + "events": [ + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [ + { + "type": { + "value": "VariableChildExists2" + }, + "parameters": [ + "Layer", + "\"Frequency\"" + ] + } + ], + "actions": [ + { + "type": { + "value": "CameraShake::SetFrequency" + }, + "parameters": [ + "", + "Layer.Frequency", + "\"\"", + "" + ] + } + ] + }, + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [ + { + "type": { + "value": "VariableChildExists2" + }, + "parameters": [ + "Layer", + "\"AmplitudeX\"" + ] + } + ], + "actions": [ + { + "type": { + "value": "SetNumberVariable" + }, + "parameters": [ + "AmplitudeX", + "=", + "Layer.AmplitudeX" + ] + } + ] + }, + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [ + { + "type": { + "value": "VariableChildExists2" + }, + "parameters": [ + "Layer", + "\"AmplitudeY\"" + ] + } + ], + "actions": [ + { + "type": { + "value": "SetNumberVariable" + }, + "parameters": [ + "AmplitudeY", + "=", + "Layer.AmplitudeY" + ] + } + ] + }, + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [ + { + "type": { + "value": "VariableChildExists2" + }, + "parameters": [ + "Layer", + "\"AmplitudeAngle\"" + ] + } + ], + "actions": [ + { + "type": { + "value": "SetNumberVariable" + }, + "parameters": [ + "AmplitudeAngle", + "=", + "Layer.AmplitudeAngle" + ] + } + ] + }, + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [ + { + "type": { + "value": "VariableChildExists2" + }, + "parameters": [ + "Layer", + "\"AmplitudeZoom\"" + ] + } + ], + "actions": [ + { + "type": { + "value": "SetNumberVariable" + }, + "parameters": [ + "AmplitudeZoom", + "=", + "Layer.AmplitudeZoom" + ] + } + ] + }, + { + "type": "BuiltinCommonInstructions::Comment", + "color": { + "b": 109, + "g": 230, + "r": 255, + "textB": 0, + "textG": 0, + "textR": 0 + }, + "comment": "Shake the layer camera.\nSave the camera displacement to revert it in onScenePostEvents." + }, + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [ + { + "type": { + "value": "NumberVariable" + }, + "parameters": [ + "AmplitudeX", + "!=", + "0" + ] + } + ], + "actions": [ + { + "type": { + "value": "SetNumberVariable" + }, + "parameters": [ + "Layers[LayerName].CameraDeltaX", + "=", + "CameraShake::Noise2d(\"\", TimeFromStart(), 1000) * AmplitudeX * EaseFactor" + ] + }, + { + "type": { + "value": "SetCameraCenterX" + }, + "parameters": [ + "", + "+", + "Layers[LayerName].CameraDeltaX", + "ActualLayerName", + "0" + ] + } + ] + }, + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [ + { + "type": { + "value": "NumberVariable" + }, + "parameters": [ + "AmplitudeY", + "!=", + "0" + ] + } + ], + "actions": [ + { + "type": { + "value": "SetNumberVariable" + }, + "parameters": [ + "Layers[LayerName].CameraDeltaY", + "=", + "CameraShake::Noise2d(\"\", TimeFromStart(), 2000) * AmplitudeY * EaseFactor" + ] + }, + { + "type": { + "value": "SetCameraCenterY" + }, + "parameters": [ + "", + "+", + "Layers[LayerName].CameraDeltaY", + "ActualLayerName", + "0" + ] + } + ] + }, + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [ + { + "type": { + "value": "NumberVariable" + }, + "parameters": [ + "AmplitudeAngle", + "!=", + "0" + ] + } + ], + "actions": [ + { + "type": { + "value": "SetNumberVariable" + }, + "parameters": [ + "Layers[LayerName].CameraDeltaAngle", + "=", + "CameraShake::Noise2d(\"\", TimeFromStart(), 3000) * AmplitudeAngle * EaseFactor" + ] + }, + { + "type": { + "value": "SetCameraAngle" + }, + "parameters": [ + "", + "+", + "Layers[LayerName].CameraDeltaAngle", + "ActualLayerName", + "0" + ] + } + ] + }, + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [ + { + "type": { + "value": "NumberVariable" + }, + "parameters": [ + "AmplitudeZoom", + "!=", + "1" + ] + } + ], + "actions": [ + { + "type": { + "value": "SetNumberVariable" + }, + "parameters": [ + "Layers[LayerName].CameraDeltaZoom", + "=", + "pow(AmplitudeZoom, CameraShake::Noise2d(\"\", TimeFromStart(), 4000) * EaseFactor)" + ] + }, + { + "type": { + "value": "ZoomCamera" + }, + "parameters": [ + "", + "CameraZoom(ActualLayerName, 0) * Layers[LayerName].CameraDeltaZoom", + "ActualLayerName", + "0" + ] + } + ] + } + ] + } + ] + } + ], + "variables": [ + { + "folded": true, + "name": "AmplitudeX", + "type": "number", + "value": 0 + }, + { + "folded": true, + "name": "AmplitudeY", + "type": "number", + "value": 0 + }, + { + "folded": true, + "name": "AmplitudeAngle", + "type": "number", + "value": 0 + }, + { + "folded": true, + "name": "AmplitudeZoom", + "type": "number", + "value": 0 + }, + { + "folded": true, + "name": "EaseFactor", + "type": "number", + "value": 1 + }, + { + "folded": true, + "name": "ActualLayerName", + "type": "string", + "value": "" + } + ] + } + ] + } + ], + "parameters": [], + "objectGroups": [] + }, + { + "fullName": "", + "functionType": "Action", + "name": "onScenePreEvents", + "sentence": "", + "events": [ + { + "type": "BuiltinCommonInstructions::Comment", + "color": { + "b": 109, + "g": 230, + "r": 255, + "textB": 0, + "textG": 0, + "textR": 0 + }, + "comment": "Revert the shaking." + }, + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [ + { + "type": { + "value": "CameraShake::IsShaking" + }, + "parameters": [ + "", + "" + ] + } + ], + "actions": [], + "events": [ + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [], + "actions": [], + "events": [ + { + "type": "BuiltinCommonInstructions::ForEachChildVariable", + "iterableVariableName": "Layers", + "valueIteratorVariableName": "Layer", + "keyIteratorVariableName": "LayerName", + "conditions": [ + { + "type": { + "value": "BooleanVariable" + }, + "parameters": [ + "Layer.Shakable", + "True", + "" + ] + } + ], + "actions": [ + { + "type": { + "value": "SetNumberVariable" + }, + "parameters": [ + "CameraDeltaX", + "=", + "Layer.CameraDeltaX" + ] + }, + { + "type": { + "value": "SetNumberVariable" + }, + "parameters": [ + "CameraDeltaY", + "=", + "Layer.CameraDeltaY" + ] + }, + { + "type": { + "value": "SetNumberVariable" + }, + "parameters": [ + "CameraDeltaAngle", + "=", + "Layer.CameraDeltaAngle" + ] + }, + { + "type": { + "value": "SetNumberVariable" + }, + "parameters": [ + "CameraDeltaZoom", + "=", + "Layer.CameraDeltaZoom" + ] + }, + { + "type": { + "value": "SetStringVariable" + }, + "parameters": [ + "ActualLayerName", + "=", + "LayerName" + ] + } + ], + "events": [ + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [ + { + "type": { + "value": "StringVariable" + }, + "parameters": [ + "LayerName", + "=", + "\"__BaseLayer\"" + ] + } + ], + "actions": [ + { + "type": { + "value": "SetStringVariable" + }, + "parameters": [ + "ActualLayerName", + "=", + "\"\"" + ] + } + ] + }, + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [ + { + "type": { + "value": "NumberVariable" + }, + "parameters": [ + "CameraDeltaX", + "!=", + "0" + ] + } + ], + "actions": [ + { + "type": { + "value": "SetCameraCenterX" + }, + "parameters": [ + "", + "-", + "CameraDeltaX", + "ActualLayerName", + "0" + ] + } + ] + }, + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [ + { + "type": { + "value": "NumberVariable" + }, + "parameters": [ + "CameraDeltaY", + "!=", + "0" + ] + } + ], + "actions": [ + { + "type": { + "value": "SetCameraCenterY" + }, + "parameters": [ + "", + "-", + "CameraDeltaY", + "ActualLayerName", + "0" + ] + } + ] + }, + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [ + { + "type": { + "value": "NumberVariable" + }, + "parameters": [ + "CameraDeltaAngle", + "!=", + "0" + ] + } + ], + "actions": [ + { + "type": { + "value": "SetCameraAngle" + }, + "parameters": [ + "", + "-", + "CameraDeltaAngle", + "ActualLayerName", + "0" + ] + } + ] + }, + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [ + { + "type": { + "value": "NumberVariable" + }, + "parameters": [ + "CameraDeltaZoom", + "!=", + "0" + ] + } + ], + "actions": [ + { + "type": { + "value": "ZoomCamera" + }, + "parameters": [ + "", + "CameraZoom(ActualLayerName) / CameraDeltaZoom", + "ActualLayerName", + "0" + ] + } + ] + } + ] + } + ], + "variables": [ + { + "folded": true, + "name": "ActualLayerName", + "type": "string", + "value": "" + }, + { + "folded": true, + "name": "CameraDeltaX", + "type": "number", + "value": 0 + }, + { + "folded": true, + "name": "CameraDeltaY", + "type": "number", + "value": 0 + }, + { + "folded": true, + "name": "CameraDeltaAngle", + "type": "number", + "value": 0 + }, + { + "folded": true, + "name": "CameraDeltaZoom", + "type": "number", + "value": 0 + } + ] + } + ] + } + ], + "parameters": [], + "objectGroups": [] + }, + { + "description": "Shake the camera on layers chosen with configuration actions.", + "fullName": "Shake camera", + "functionType": "Action", + "name": "ShakeCamera", + "sentence": "Shake camera for _PARAM1_ seconds with _PARAM2_ seconds of easing to start and _PARAM3_ seconds to stop", + "events": [ + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [], + "actions": [ + { + "type": { + "value": "SetNumberVariable" + }, + "parameters": [ + "Time", + "=", + "0" + ] + }, + { + "type": { + "value": "SetNumberVariable" + }, + "parameters": [ + "Duration", + "=", + "NewDuration" + ] + }, + { + "type": { + "value": "SetNumberVariable" + }, + "parameters": [ + "StartEaseDuration", + "=", + "NewStartEaseDuration" + ] + }, + { + "type": { + "value": "SetNumberVariable" + }, + "parameters": [ + "StopEaseDuration", + "=", + "NewStopEaseDuration" + ] + } + ] + }, + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [ + { + "type": { + "value": "NumberVariable" + }, + "parameters": [ + "Duration", + "<", + "StartEaseDuration + StopEaseDuration" + ] + } + ], + "actions": [ + { + "type": { + "value": "SetNumberVariable" + }, + "parameters": [ + "StartEaseDuration", + "=", + "StartEaseDuration * Duration / (StartEaseDuration + StopEaseDuration)" + ] + }, + { + "type": { + "value": "SetNumberVariable" + }, + "parameters": [ + "StopEaseDuration", + "=", + "StopEaseDuration * Duration / (StartEaseDuration + StopEaseDuration)" + ] + } + ] + } + ], + "parameters": [ + { + "description": "Duration (in seconds)", + "name": "NewDuration", + "type": "expression" + }, + { + "description": "Ease duration to start (in seconds)", + "name": "NewStartEaseDuration", + "type": "expression" + }, + { + "description": "Ease duration to stop (in seconds)", + "name": "NewStopEaseDuration", + "type": "expression" + } + ], + "objectGroups": [] + }, + { + "description": "Shake the camera on the specified layer, using one or more ways to shake (position, angle, zoom). This action is deprecated. Please use the other one with the same name.", + "fullName": "Shake camera (deprecated)", + "functionType": "Action", + "name": "CameraShake", + "private": true, + "sentence": "Shake camera on _PARAM3_ layer for _PARAM5_ seconds. Use an amplitude of _PARAM1_px on X axis and _PARAM2_px on Y axis, angle rotation amplitude _PARAM6_ degrees, and zoom amplitude _PARAM7_ percent. Wait _PARAM8_ seconds between shakes. Keep shaking until stopped: _PARAM9_", + "events": [ + { + "colorB": 228, + "colorG": 176, + "colorR": 74, + "creationTime": 0, + "name": "Camera Shake", + "source": "", + "type": "BuiltinCommonInstructions::Group", + "events": [ + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [], + "actions": [ + { + "type": { + "value": "ClearVariableChildren" + }, + "parameters": [ + "Layers" + ] + }, + { + "type": { + "value": "CameraShake::SetLayerShakable" + }, + "parameters": [ + "", + "", + "NewLayer", + "" + ] + } + ] + }, + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [], + "actions": [ + { + "type": { + "value": "SetNumberVariable" + }, + "parameters": [ + "Time", + "=", + "0" + ] + }, + { + "type": { + "value": "SetStringVariable" + }, + "parameters": [ + "Layer", + "=", + "NewLayer" + ] + }, + { + "type": { + "value": "SetNumberVariable" + }, + "parameters": [ + "Duration", + "=", + "NewDuration" + ] + }, + { + "type": { + "value": "SetNumberVariable" + }, + "parameters": [ + "StartEaseDuration", + "=", + "0" + ] + }, + { + "type": { + "value": "SetNumberVariable" + }, + "parameters": [ + "StopEaseDuration", + "=", + "NewDuration" + ] + }, + { + "type": { + "value": "SetNumberVariable" + }, + "parameters": [ + "DefaultAmplitudeX", + "=", + "abs(AmplitudeX)" + ] + }, + { + "type": { + "value": "SetNumberVariable" + }, + "parameters": [ + "DefaultAmplitudeY", + "=", + "abs(AmplitudeY)" + ] + }, + { + "type": { + "value": "SetNumberVariable" + }, + "parameters": [ + "DefaultAmplitudeAngle", + "=", + "AmplitudeAngle" + ] + }, + { + "type": { + "value": "SetNumberVariable" + }, + "parameters": [ + "DefaultAmplitudeZoom", + "=", + "1 + AmplitudeZoom / 100" + ] + } + ], + "events": [ + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [ + { + "type": { + "value": "BuiltinCommonInstructions::CompareNumbers" + }, + "parameters": [ + "ShakePeriod", + "!=", + "0" + ] + } + ], + "actions": [ + { + "type": { + "value": "SetNumberVariable" + }, + "parameters": [ + "DefaultFrequency", + "=", + "1 / ShakePeriod" + ] + } + ] + }, + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [ + { + "type": { + "value": "BuiltinCommonInstructions::CompareNumbers" + }, + "parameters": [ + "ShakePeriod", + "=", + "0" + ] + } + ], + "actions": [ + { + "type": { + "value": "SetNumberVariable" + }, + "parameters": [ + "DefaultFrequency", + "=", + "1 / 0.08" + ] + } + ] + } + ] + }, + { + "type": "BuiltinCommonInstructions::Comment", + "color": { + "b": 109, + "g": 230, + "r": 255, + "textB": 0, + "textG": 0, + "textR": 0 + }, + "comment": "Determine if the shake should keep going until stopped" + }, + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [ + { + "type": { + "value": "BooleanVariable" + }, + "parameters": [ + "ShakeForever", + "True", + "" + ] + } + ], + "actions": [ + { + "type": { + "value": "SetNumberVariable" + }, + "parameters": [ + "Duration", + "=", + "1234567890" + ] + } + ] + }, + { + "type": "BuiltinCommonInstructions::Comment", + "color": { + "b": 109, + "g": 230, + "r": 255, + "textB": 0, + "textG": 0, + "textR": 0 + }, + "comment": "Add default values if none were provided" + }, + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [ + { + "type": { + "value": "NumberVariable" + }, + "parameters": [ + "NewDuration", + "=", + "0" + ] + } + ], + "actions": [ + { + "type": { + "value": "SetNumberVariable" + }, + "parameters": [ + "Duration", + "=", + "0.5" + ] + } + ] + } + ], + "parameters": [] + } + ], + "parameters": [ + { + "description": "Amplitude of shaking on the X axis (in pixels)", + "name": "AmplitudeX", + "type": "expression" + }, + { + "description": "Amplitude of shaking on the Y axis (in pixels)", + "name": "AmplitudeY", + "type": "expression" + }, + { + "description": "Layer (base layer if empty)", + "name": "NewLayer", + "type": "layer" + }, + { + "description": "Camera index (Default: 0)", + "name": "Camera", + "type": "expression" + }, + { + "description": "Duration (in seconds) (Default: 0.5)", + "name": "NewDuration", + "type": "expression" + }, + { + "description": "Angle rotation amplitude (in degrees) (For example: 2)", + "name": "AmplitudeAngle", + "type": "expression" + }, + { + "description": "Zoom factor amplitude", + "name": "AmplitudeZoom", + "type": "expression" + }, + { + "description": "Period between shakes (in seconds) (Default: 0.08)", + "name": "ShakePeriod", + "type": "expression" + }, + { + "description": "Keep shaking until stopped", + "longDescription": "Duration value will be ignored", + "name": "ShakeForever", + "type": "yesorno" + } + ], + "objectGroups": [] + }, + { + "description": "Start shaking the camera indefinitely.", + "fullName": "Start camera shaking", + "functionType": "Action", + "name": "StartShaking", + "sentence": "Start shaking the camera with _PARAM1_ seconds of easing", + "events": [ + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [], + "actions": [ + { + "type": { + "value": "SetNumberVariable" + }, + "parameters": [ + "Time", + "=", + "0" + ] + }, + { + "type": { + "value": "SetNumberVariable" + }, + "parameters": [ + "Duration", + "=", + "1234567890" + ] + }, + { + "type": { + "value": "SetNumberVariable" + }, + "parameters": [ + "StartEaseDuration", + "=", + "EaseDuration" + ] + } + ] + } + ], + "parameters": [ + { + "description": "Ease duration (in seconds)", + "name": "EaseDuration", + "type": "expression" + } + ], + "objectGroups": [] + }, + { + "description": "Stop shaking the camera.", + "fullName": "Stop camera shaking", + "functionType": "Action", + "name": "StopShaking", + "sentence": "Stop shaking the camera with _PARAM1_ seconds of easing", + "events": [ + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [], + "actions": [ + { + "type": { + "value": "SetNumberVariable" + }, + "parameters": [ + "Time", + "=", + "0" + ] + }, + { + "type": { + "value": "SetNumberVariable" + }, + "parameters": [ + "Duration", + "=", + "EaseDuration" + ] + }, + { + "type": { + "value": "SetNumberVariable" + }, + "parameters": [ + "StopEaseDuration", + "=", + "EaseDuration" + ] + } + ] + } + ], + "parameters": [ + { + "description": "Ease duration (in seconds)", + "name": "EaseDuration", + "type": "expression" + } + ], + "objectGroups": [] + }, + { + "description": "Mark a layer as shakable.", + "fullName": "Shakable layer", + "functionType": "Action", + "group": "Camera shake configuration", + "name": "SetLayerShakable", + "sentence": "Mark the layer: _PARAM2_ as shakable: _PARAM1_", + "events": [ + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [], + "actions": [ + { + "type": { + "value": "SetStringVariable" + }, + "parameters": [ + "LayerName", + "=", + "NewLayerName" + ] + } + ] + }, + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [ + { + "type": { + "value": "BuiltinCommonInstructions::CompareStrings" + }, + "parameters": [ + "NewLayerName", + "=", + "\"\"" + ] + } + ], + "actions": [ + { + "type": { + "value": "SetStringVariable" + }, + "parameters": [ + "LayerName", + "=", + "\"__BaseLayer\"" + ] + } + ] + }, + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [ + { + "type": { + "inverted": true, + "value": "BooleanVariable" + }, + "parameters": [ + "Shakable", + "True", + "" + ] + } + ], + "actions": [ + { + "type": { + "value": "SetBooleanVariable" + }, + "parameters": [ + "Layers[LayerName].Shakable", + "False", + "" + ] + } + ] + }, + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [ + { + "type": { + "value": "BooleanVariable" + }, + "parameters": [ + "Shakable", + "True", + "" + ] + } + ], + "actions": [ + { + "type": { + "value": "SetBooleanVariable" + }, + "parameters": [ + "Layers[LayerName].Shakable", + "True", + "" + ] + } + ] + } + ], + "parameters": [ + { + "defaultValue": "yes", + "description": "Shakable", + "name": "Shakable", + "optional": true, + "type": "yesorno" + }, + { + "description": "Layer", + "name": "NewLayerName", + "type": "layer" + } + ], + "objectGroups": [] + }, + { + "description": "Check if the camera is shaking.", + "fullName": "Camera is shaking", + "functionType": "Condition", + "name": "IsShaking", + "sentence": "Camera is shaking", + "events": [ + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [ + { + "type": { + "value": "NumberVariable" + }, + "parameters": [ + "Time", + "<", + "Duration" + ] + } + ], + "actions": [ + { + "type": { + "value": "SetReturnBoolean" + }, + "parameters": [ + "True" + ] + } + ] + } + ], + "parameters": [], + "objectGroups": [] + }, + { + "description": "Change the translation amplitude of the shaking (in pixels).", + "fullName": "Layer translation amplitude", + "functionType": "Action", + "group": "Camera shake configuration", + "name": "SetLayerTranslationAmplitude", + "sentence": "Change the translation amplitude of the shaking to _PARAM1_; _PARAM2_ (layer: _PARAM3_)", + "events": [ + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [], + "actions": [ + { + "type": { + "value": "SetStringVariable" + }, + "parameters": [ + "LayerName", + "=", + "NewLayerName" + ] + } + ] + }, + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [ + { + "type": { + "value": "BuiltinCommonInstructions::CompareStrings" + }, + "parameters": [ + "NewLayerName", + "=", + "\"\"" + ] + } + ], + "actions": [ + { + "type": { + "value": "SetStringVariable" + }, + "parameters": [ + "LayerName", + "=", + "\"__BaseLayer\"" + ] + } + ] + }, + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [], + "actions": [ + { + "type": { + "value": "SetNumberVariable" + }, + "parameters": [ + "Layers[LayerName].AmplitudeX", + "=", + "AmplitudeX" + ] + }, + { + "type": { + "value": "SetNumberVariable" + }, + "parameters": [ + "Layers[LayerName].AmplitudeY", + "=", + "AmplitudeY" + ] + } + ] + } + ], + "parameters": [ + { + "description": "Amplitude of shaking on the X axis (in pixels)", + "name": "AmplitudeX", + "type": "expression" + }, + { + "description": "Amplitude of shaking on the Y axis (in pixels)", + "name": "AmplitudeY", + "type": "expression" + }, + { + "description": "Layer", + "name": "NewLayerName", + "type": "layer" + } + ], + "objectGroups": [] + }, + { + "description": "Change the rotation amplitude of the shaking (in degrees).", + "fullName": "Layer rotation amplitude", + "functionType": "Action", + "group": "Camera shake configuration", + "name": "SetLayerRotationAmplitude", + "sentence": "Change the rotation amplitude of the shaking to _PARAM1_ degrees (layer: _PARAM2_)", + "events": [ + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [], + "actions": [ + { + "type": { + "value": "SetStringVariable" + }, + "parameters": [ + "LayerName", + "=", + "NewLayerName" + ] + } + ] + }, + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [ + { + "type": { + "value": "BuiltinCommonInstructions::CompareStrings" + }, + "parameters": [ + "NewLayerName", + "=", + "\"\"" + ] + } + ], + "actions": [ + { + "type": { + "value": "SetStringVariable" + }, + "parameters": [ + "LayerName", + "=", + "\"__BaseLayer\"" + ] + } + ] + }, + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [], + "actions": [ + { + "type": { + "value": "SetNumberVariable" + }, + "parameters": [ + "Layers[LayerName].AmplitudeAngle", + "=", + "AmplitudeAngle" + ] + } + ] + } + ], + "parameters": [ + { + "description": "Angle (in degree)", + "name": "AmplitudeAngle", + "type": "expression" + }, + { + "description": "NewLayerName", + "name": "NewLayerName", + "type": "layer" + } + ], + "objectGroups": [] + }, + { + "description": "Change the zoom factor amplitude of the shaking. The shaking will zoom and unzoom by this factor (for instance 1.0625 is a valid value).", + "fullName": "Layer zoom amplitude", + "functionType": "Action", + "group": "Camera shake configuration", + "name": "SetLayerZoomAmplitude", + "sentence": "Change the zoom factor amplitude of the shaking to _PARAM1_ (layer: _PARAM2_)", + "events": [ + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [], + "actions": [ + { + "type": { + "value": "SetStringVariable" + }, + "parameters": [ + "LayerName", + "=", + "NewLayerName" + ] + } + ] + }, + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [ + { + "type": { + "value": "BuiltinCommonInstructions::CompareStrings" + }, + "parameters": [ + "NewLayerName", + "=", + "\"\"" + ] + } + ], + "actions": [ + { + "type": { + "value": "SetStringVariable" + }, + "parameters": [ + "LayerName", + "=", + "\"__BaseLayer\"" + ] + } + ] + }, + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [], + "actions": [ + { + "type": { + "value": "SetNumberVariable" + }, + "parameters": [ + "Layers[LayerName].AmplitudeZoom", + "=", + "AmplitudeZoom" + ] + } + ] + } + ], + "parameters": [ + { + "description": "Zoom factor", + "name": "AmplitudeZoom", + "type": "expression" + }, + { + "description": "NewLayerName", + "name": "NewLayerName", + "type": "layer" + } + ], + "objectGroups": [] + }, + { + "description": "Change the number of back and forth per seconds.", + "fullName": "Layer shaking frequency", + "functionType": "Action", + "group": "Camera shake configuration", + "name": "SetLayerShakingFrequency", + "sentence": "Change the shaking frequency to _PARAM1_ (layer: _PARAM2_)", + "events": [ + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [], + "actions": [ + { + "type": { + "value": "SetStringVariable" + }, + "parameters": [ + "LayerName", + "=", + "NewLayerName" + ] + } + ] + }, + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [ + { + "type": { + "value": "BuiltinCommonInstructions::CompareStrings" + }, + "parameters": [ + "NewLayerName", + "=", + "\"\"" + ] + } + ], + "actions": [ + { + "type": { + "value": "SetStringVariable" + }, + "parameters": [ + "LayerName", + "=", + "\"__BaseLayer\"" + ] + } + ] + }, + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [], + "actions": [ + { + "type": { + "value": "SetNumberVariable" + }, + "parameters": [ + "Layers[LayerName].Frequency", + "=", + "Frequency" + ] + } + ] + } + ], + "parameters": [ + { + "description": "Frequency", + "name": "Frequency", + "type": "expression" + }, + { + "description": "NewLayerName", + "name": "NewLayerName", + "type": "layer" + } + ], + "objectGroups": [] + }, + { + "description": "Change the default translation amplitude of the shaking (in pixels).", + "fullName": "Default translation amplitude", + "functionType": "Action", + "group": "Camera shake configuration", + "name": "SetDefaultTranslationAmplitude", + "sentence": "Change the default translation amplitude of the shaking to _PARAM1_; _PARAM2_", + "events": [ + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [], + "actions": [ + { + "type": { + "value": "SetNumberVariable" + }, + "parameters": [ + "DefaultAmplitudeX", + "=", + "AmplitudeX" + ] + }, + { + "type": { + "value": "SetNumberVariable" + }, + "parameters": [ + "DefaultAmplitudeY", + "=", + "AmplitudeY" + ] + } + ] + } + ], + "parameters": [ + { + "description": "Amplitude of shaking on the X axis (in pixels)", + "name": "AmplitudeX", + "type": "expression" + }, + { + "description": "Amplitude of shaking on the Y axis (in pixels)", + "name": "AmplitudeY", + "type": "expression" + } + ], + "objectGroups": [] + }, + { + "description": "Change the default rotation amplitude of the shaking (in degrees).", + "fullName": "Default rotation amplitude", + "functionType": "Action", + "group": "Camera shake configuration", + "name": "SetDefaultRotationAmplitude", + "sentence": "Change the default rotation amplitude of the shaking to _PARAM1_ degrees", + "events": [ + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [], + "actions": [ + { + "type": { + "value": "SetNumberVariable" + }, + "parameters": [ + "DefaultAmplitudeAngle", + "=", + "AmplitudeAngle" + ] + } + ] + } + ], + "parameters": [ + { + "description": "Angle (in degree)", + "name": "AmplitudeAngle", + "type": "expression" + } + ], + "objectGroups": [] + }, + { + "description": "Change the default zoom factor amplitude of the shaking. The shaking will zoom and unzoom by this factor (for instance 1.0625 is a valid value).", + "fullName": "Default zoom amplitude", + "functionType": "Action", + "group": "Camera shake configuration", + "name": "SetDefaultZoomAmplitude", + "sentence": "Change the default zoom factor amplitude of the shaking to _PARAM1_", + "events": [ + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [], + "actions": [ + { + "type": { + "value": "SetNumberVariable" + }, + "parameters": [ + "DefaultAmplitudeZoom", + "=", + "AmplitudeZoom" + ] + } + ] + } + ], + "parameters": [ + { + "description": "Zoom factor", + "name": "AmplitudeZoom", + "type": "expression" + } + ], + "objectGroups": [] + }, + { + "description": "Change the default number of back and forth per seconds.", + "fullName": "Default shaking frequency", + "functionType": "Action", + "group": "Camera shake configuration", + "name": "SetDefaultShakingFrequency", + "sentence": "Change the default shaking frequency to _PARAM1_", + "events": [ + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [], + "actions": [ + { + "type": { + "value": "SetNumberVariable" + }, + "parameters": [ + "DefaultFrequency", + "=", + "Frequency" + ] + } + ] + } + ], + "parameters": [ + { + "description": "Frequency", + "name": "Frequency", + "type": "expression" + } + ], + "objectGroups": [] + }, + { + "fullName": "", + "functionType": "Action", + "name": "onFirstSceneLoaded", + "sentence": "", + "events": [ + { + "type": "BuiltinCommonInstructions::JsCode", + "inlineCode": [ + "if (gdjs._cameraShakeExtension) {", + " return;", + "}", + "", + "/** Noise generator manager. */", + "class NoiseManager {", + " /**", + " * Create the manager of noise generators.", + " */", + " constructor() {", + " this.seed = gdjs.randomInRange(1, Number.MAX_SAFE_INTEGER);", + " /** @type {Map} */", + " this.generators = new Map();", + " }", + "", + " /**", + " * @param name {string}", + " * @return {NoiseGenerator}", + " */", + " getGenerator(name) {", + " let generator = this.generators.get(name);", + " if (!generator) {", + " generator = new NoiseGenerator(name + this.seed);", + " this.generators.set(name, generator);", + " }", + " return generator;", + " }", + "", + " /**", + " * @param seed {number}", + " */", + " setSeed(seed) {", + " this.seed = seed;", + " this.generators.forEach(generator => generator.setSeed(name + this.seed));", + " }", + "", + " /**", + " * @param name {string}", + " */", + " deleteGenerator(name) {", + " this.generators.delete(name);", + " }", + "", + " /**", + " */", + " deleteAllGenerators() {", + " this.generators.clear();", + " }", + "}", + "", + "/** Noise generator with octaves. */", + "class NoiseGenerator {", + " /**", + " * Create a noise generator with a seed.", + " * @param seed {string}", + " */", + " constructor(seed) {", + " this.simplexNoise = new SimplexNoise(seed);", + " this.frequency = 1;", + " this.octaves = 1;", + " this.persistence = 0.5;", + " this.lacunarity = 2;", + " this.xLoopPeriod = 0;", + " this.yLoopPeriod = 0;", + " }", + "", + " /**", + " * @param seed {string}", + " */", + " setSeed(seed) {", + " this.simplexNoise = new SimplexNoise(seed);", + " }", + "", + " /**", + " * @param x {float}", + " * @param y {float}", + " * @param z {float} optionnal", + " * @param w {float} optionnal", + " * @return {float}", + " */", + " noise(x, y, z, w) {", + " if (this.xLoopPeriod && this.yLoopPeriod) {", + " const circleRatioX = 2 * Math.PI / this.xLoopPeriod;", + " const circleRatioY = 2 * Math.PI / this.yLoopPeriod;", + " const angleX = circleRatioX * x;", + " const angleY = circleRatioY * y;", + " x = Math.cos(angleX) / circleRatioX;", + " y = Math.sin(angleX) / circleRatioX;", + " z = Math.cos(angleY) / circleRatioY;", + " w = Math.sin(angleY) / circleRatioY;", + " }", + " else if (this.xLoopPeriod) {", + " const circleRatio = 2 * Math.PI / this.xLoopPeriod;", + " const angleX = circleRatio * x;", + " w = z;", + " z = y;", + " x = Math.cos(angleX) / circleRatio;", + " y = Math.sin(angleX) / circleRatio;", + " }", + " else if (this.yLoopPeriod) {", + " const circleRatio = 2 * Math.PI / this.xLoopPeriod;", + " const angleX = circleRatio * x;", + " w = z;", + " // Make the circle perimeter equals to the looping period", + " // to keep the same perceived frequency with or without looping.", + " y = Math.cos(angleX) / circleRatio;", + " z = Math.sin(angleX) / circleRatio;", + " }", + " let noiseFunction = this.simplexNoise.noise4D.bind(this.simplexNoise);", + " if (z === undefined) {", + " noiseFunction = this.simplexNoise.noise2D.bind(this.simplexNoise);", + " }", + " else if (w === undefined) {", + " noiseFunction = this.simplexNoise.noise3D.bind(this.simplexNoise);", + " }", + " let frequency = this.frequency;", + " let noiseSum = 0;", + " let amplitudeSum = 0;", + " let amplitude = 1;", + " for (let i = 0; i < this.octaves; i++) {", + " noiseSum += noiseFunction(x * frequency, y * frequency, z * frequency, w * frequency) * amplitude;", + " amplitudeSum += Math.abs(amplitude);", + " amplitude *= this.persistence;", + " frequency *= this.lacunarity;", + " }", + " return noiseSum / amplitudeSum;", + " }", + "}", + "", + "/*", + "A fast javascript implementation of simplex noise by Jonas Wagner", + "https://github.com/jwagner/simplex-noise.js", + "", + "Based on a speed-improved simplex noise algorithm for 2D, 3D and 4D in Java.", + "Which is based on example code by Stefan Gustavson (stegu@itn.liu.se).", + "With Optimisations by Peter Eastman (peastman@drizzle.stanford.edu).", + "Better rank ordering method by Stefan Gustavson in 2012.", + "", + " Copyright (c) 2021 Jonas Wagner", + "", + " Permission is hereby granted, free of charge, to any person obtaining a copy", + " of this software and associated documentation files (the \"Software\"), to deal", + " in the Software without restriction, including without limitation the rights", + " to use, copy, modify, merge, publish, distribute, sublicense, and/or sell", + " copies of the Software, and to permit persons to whom the Software is", + " furnished to do so, subject to the following conditions:", + "", + " The above copyright notice and this permission notice shall be included in all", + " copies or substantial portions of the Software.", + "", + " THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR", + " IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,", + " FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE", + " AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER", + " LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,", + " OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE", + " SOFTWARE.", + " */", + "", + "const F2 = 0.5 * (Math.sqrt(3.0) - 1.0);", + "const G2 = (3.0 - Math.sqrt(3.0)) / 6.0;", + "const F3 = 1.0 / 3.0;", + "const G3 = 1.0 / 6.0;", + "const F4 = (Math.sqrt(5.0) - 1.0) / 4.0;", + "const G4 = (5.0 - Math.sqrt(5.0)) / 20.0;", + "const grad3 = new Float32Array([1, 1, 0,", + " -1, 1, 0,", + " 1, -1, 0,", + " -1, -1, 0,", + " 1, 0, 1,", + " -1, 0, 1,", + " 1, 0, -1,", + " -1, 0, -1,", + " 0, 1, 1,", + " 0, -1, 1,", + " 0, 1, -1,", + " 0, -1, -1]);", + "const grad4 = new Float32Array([0, 1, 1, 1, 0, 1, 1, -1, 0, 1, -1, 1, 0, 1, -1, -1,", + " 0, -1, 1, 1, 0, -1, 1, -1, 0, -1, -1, 1, 0, -1, -1, -1,", + " 1, 0, 1, 1, 1, 0, 1, -1, 1, 0, -1, 1, 1, 0, -1, -1,", + " -1, 0, 1, 1, -1, 0, 1, -1, -1, 0, -1, 1, -1, 0, -1, -1,", + " 1, 1, 0, 1, 1, 1, 0, -1, 1, -1, 0, 1, 1, -1, 0, -1,", + " -1, 1, 0, 1, -1, 1, 0, -1, -1, -1, 0, 1, -1, -1, 0, -1,", + " 1, 1, 1, 0, 1, 1, -1, 0, 1, -1, 1, 0, 1, -1, -1, 0,", + " -1, 1, 1, 0, -1, 1, -1, 0, -1, -1, 1, 0, -1, -1, -1, 0]);", + "", + "", + "/**", + " * Builds a random permutation table.", + " * This is exported only for (internal) testing purposes.", + " * Do not rely on this export.", + " * @param {() => number} random", + " * @private", + " */", + "function buildPermutationTable(random) {", + " const p = new Uint8Array(256);", + " for (let i = 0; i < 256; i++) {", + " p[i] = i;", + " }", + " for (let i = 0; i < 255; i++) {", + " const r = i + ~~(random() * (256 - i));", + " const aux = p[i];", + " p[i] = p[r];", + " p[r] = aux;", + " }", + " return p;", + "}", + "", + "/*", + "The ALEA PRNG and masher code used by simplex-noise.js", + "is based on code by Johannes Baagøe, modified by Jonas Wagner.", + "See alea.md for the full license.", + "@param {string|number} seed", + "*/", + "function alea(seed) {", + " let s0 = 0;", + " let s1 = 0;", + " let s2 = 0;", + " let c = 1;", + " const mash = masher();", + " s0 = mash(' ');", + " s1 = mash(' ');", + " s2 = mash(' ');", + " s0 -= mash(seed);", + " if (s0 < 0) {", + " s0 += 1;", + " }", + " s1 -= mash(seed);", + " if (s1 < 0) {", + " s1 += 1;", + " }", + " s2 -= mash(seed);", + " if (s2 < 0) {", + " s2 += 1;", + " }", + " return function () {", + " const t = 2091639 * s0 + c * 2.3283064365386963e-10; // 2^-32", + " s0 = s1;", + " s1 = s2;", + " return s2 = t - (c = t | 0);", + " };", + "}", + "", + "function masher() {", + " let n = 0xefc8249d;", + " return function (data) {", + " data = data.toString();", + " for (let i = 0; i < data.length; i++) {", + " n += data.charCodeAt(i);", + " let h = 0.02519603282416938 * n;", + " n = h >>> 0;", + " h -= n;", + " h *= n;", + " n = h >>> 0;", + " h -= n;", + " n += h * 0x100000000; // 2^32", + " }", + " return (n >>> 0) * 2.3283064365386963e-10; // 2^-32", + " };", + "}", + "", + "/** Deterministic simplex noise generator suitable for 2D, 3D and 4D spaces. */", + "class SimplexNoise {", + " /**", + " * Creates a new `SimplexNoise` instance.", + " * This involves some setup. You can save a few cpu cycles by reusing the same instance.", + " * @param {(() => number)|string|number} randomOrSeed A random number generator or a seed (string|number).", + " * Defaults to Math.random (random irreproducible initialization).", + " */", + " constructor(randomOrSeed) {", + " if (randomOrSeed === void 0) { randomOrSeed = Math.random; }", + " const random = typeof randomOrSeed == 'function' ? randomOrSeed : alea(randomOrSeed);", + " this.p = buildPermutationTable(random);", + " this.perm = new Uint8Array(512);", + " this.permMod12 = new Uint8Array(512);", + " for (let i = 0; i < 512; i++) {", + " this.perm[i] = this.p[i & 255];", + " this.permMod12[i] = this.perm[i] % 12;", + " }", + " }", + "", + " /**", + " * Samples the noise field in 2 dimensions", + " * @param {number} x", + " * @param {number} y", + " * @returns a number in the interval [-1, 1]", + " */", + " noise2D(x, y) {", + " const permMod12 = this.permMod12;", + " const perm = this.perm;", + " let n0 = 0; // Noise contributions from the three corners", + " let n1 = 0;", + " let n2 = 0;", + " // Skew the input space to determine which simplex cell we're in", + " const s = (x + y) * F2; // Hairy factor for 2D", + " const i = Math.floor(x + s);", + " const j = Math.floor(y + s);", + " const t = (i + j) * G2;", + " const X0 = i - t; // Unskew the cell origin back to (x,y) space", + " const Y0 = j - t;", + " const x0 = x - X0; // The x,y distances from the cell origin", + " const y0 = y - Y0;", + " // For the 2D case, the simplex shape is an equilateral triangle.", + " // Determine which simplex we are in.", + " let i1, j1; // Offsets for second (middle) corner of simplex in (i,j) coords", + " if (x0 > y0) {", + " i1 = 1;", + " j1 = 0;", + " } // lower triangle, XY order: (0,0)->(1,0)->(1,1)", + " else {", + " i1 = 0;", + " j1 = 1;", + " } // upper triangle, YX order: (0,0)->(0,1)->(1,1)", + " // A step of (1,0) in (i,j) means a step of (1-c,-c) in (x,y), and", + " // a step of (0,1) in (i,j) means a step of (-c,1-c) in (x,y), where", + " // c = (3-sqrt(3))/6", + " const x1 = x0 - i1 + G2; // Offsets for middle corner in (x,y) unskewed coords", + " const y1 = y0 - j1 + G2;", + " const x2 = x0 - 1.0 + 2.0 * G2; // Offsets for last corner in (x,y) unskewed coords", + " const y2 = y0 - 1.0 + 2.0 * G2;", + " // Work out the hashed gradient indices of the three simplex corners", + " const ii = i & 255;", + " const jj = j & 255;", + " // Calculate the contribution from the three corners", + " let t0 = 0.5 - x0 * x0 - y0 * y0;", + " if (t0 >= 0) {", + " const gi0 = permMod12[ii + perm[jj]] * 3;", + " t0 *= t0;", + " n0 = t0 * t0 * (grad3[gi0] * x0 + grad3[gi0 + 1] * y0); // (x,y) of grad3 used for 2D gradient", + " }", + " let t1 = 0.5 - x1 * x1 - y1 * y1;", + " if (t1 >= 0) {", + " const gi1 = permMod12[ii + i1 + perm[jj + j1]] * 3;", + " t1 *= t1;", + " n1 = t1 * t1 * (grad3[gi1] * x1 + grad3[gi1 + 1] * y1);", + " }", + " let t2 = 0.5 - x2 * x2 - y2 * y2;", + " if (t2 >= 0) {", + " const gi2 = permMod12[ii + 1 + perm[jj + 1]] * 3;", + " t2 *= t2;", + " n2 = t2 * t2 * (grad3[gi2] * x2 + grad3[gi2 + 1] * y2);", + " }", + " // Add contributions from each corner to get the final noise value.", + " // The result is scaled to return values in the interval [-1,1].", + " return 70.0 * (n0 + n1 + n2);", + " }", + "", + " /**", + " * Samples the noise field in 3 dimensions", + " * @param {number} x", + " * @param {number} y", + " * @param {number} z", + " * @returns a number in the interval [-1, 1]", + " */", + " noise3D(x, y, z) {", + " const permMod12 = this.permMod12;", + " const perm = this.perm;", + " let n0, n1, n2, n3; // Noise contributions from the four corners", + " // Skew the input space to determine which simplex cell we're in", + " const s = (x + y + z) * F3; // Very nice and simple skew factor for 3D", + " const i = Math.floor(x + s);", + " const j = Math.floor(y + s);", + " const k = Math.floor(z + s);", + " const t = (i + j + k) * G3;", + " const X0 = i - t; // Unskew the cell origin back to (x,y,z) space", + " const Y0 = j - t;", + " const Z0 = k - t;", + " const x0 = x - X0; // The x,y,z distances from the cell origin", + " const y0 = y - Y0;", + " const z0 = z - Z0;", + " // For the 3D case, the simplex shape is a slightly irregular tetrahedron.", + " // Determine which simplex we are in.", + " let i1, j1, k1; // Offsets for second corner of simplex in (i,j,k) coords", + " let i2, j2, k2; // Offsets for third corner of simplex in (i,j,k) coords", + " if (x0 >= y0) {", + " if (y0 >= z0) {", + " i1 = 1;", + " j1 = 0;", + " k1 = 0;", + " i2 = 1;", + " j2 = 1;", + " k2 = 0;", + " } // X Y Z order", + " else if (x0 >= z0) {", + " i1 = 1;", + " j1 = 0;", + " k1 = 0;", + " i2 = 1;", + " j2 = 0;", + " k2 = 1;", + " } // X Z Y order", + " else {", + " i1 = 0;", + " j1 = 0;", + " k1 = 1;", + " i2 = 1;", + " j2 = 0;", + " k2 = 1;", + " } // Z X Y order", + " }", + " else { // x0 y0)", + " rankx++;", + " else", + " ranky++;", + " if (x0 > z0)", + " rankx++;", + " else", + " rankz++;", + " if (x0 > w0)", + " rankx++;", + " else", + " rankw++;", + " if (y0 > z0)", + " ranky++;", + " else", + " rankz++;", + " if (y0 > w0)", + " ranky++;", + " else", + " rankw++;", + " if (z0 > w0)", + " rankz++;", + " else", + " rankw++;", + " // simplex[c] is a 4-vector with the numbers 0, 1, 2 and 3 in some order.", + " // Many values of c will never occur, since e.g. x>y>z>w makes x= 3 ? 1 : 0;", + " const j1 = ranky >= 3 ? 1 : 0;", + " const k1 = rankz >= 3 ? 1 : 0;", + " const l1 = rankw >= 3 ? 1 : 0;", + " // The integer offsets for the third simplex corner", + " const i2 = rankx >= 2 ? 1 : 0;", + " const j2 = ranky >= 2 ? 1 : 0;", + " const k2 = rankz >= 2 ? 1 : 0;", + " const l2 = rankw >= 2 ? 1 : 0;", + " // The integer offsets for the fourth simplex corner", + " const i3 = rankx >= 1 ? 1 : 0;", + " const j3 = ranky >= 1 ? 1 : 0;", + " const k3 = rankz >= 1 ? 1 : 0;", + " const l3 = rankw >= 1 ? 1 : 0;", + " // The fifth corner has all coordinate offsets = 1, so no need to compute that.", + " const x1 = x0 - i1 + G4; // Offsets for second corner in (x,y,z,w) coords", + " const y1 = y0 - j1 + G4;", + " const z1 = z0 - k1 + G4;", + " const w1 = w0 - l1 + G4;", + " const x2 = x0 - i2 + 2.0 * G4; // Offsets for third corner in (x,y,z,w) coords", + " const y2 = y0 - j2 + 2.0 * G4;", + " const z2 = z0 - k2 + 2.0 * G4;", + " const w2 = w0 - l2 + 2.0 * G4;", + " const x3 = x0 - i3 + 3.0 * G4; // Offsets for fourth corner in (x,y,z,w) coords", + " const y3 = y0 - j3 + 3.0 * G4;", + " const z3 = z0 - k3 + 3.0 * G4;", + " const w3 = w0 - l3 + 3.0 * G4;", + " const x4 = x0 - 1.0 + 4.0 * G4; // Offsets for last corner in (x,y,z,w) coords", + " const y4 = y0 - 1.0 + 4.0 * G4;", + " const z4 = z0 - 1.0 + 4.0 * G4;", + " const w4 = w0 - 1.0 + 4.0 * G4;", + " // Work out the hashed gradient indices of the five simplex corners", + " const ii = i & 255;", + " const jj = j & 255;", + " const kk = k & 255;", + " const ll = l & 255;", + " // Calculate the contribution from the five corners", + " let t0 = 0.6 - x0 * x0 - y0 * y0 - z0 * z0 - w0 * w0;", + " if (t0 < 0)", + " n0 = 0.0;", + " else {", + " const gi0 = (perm[ii + perm[jj + perm[kk + perm[ll]]]] % 32) * 4;", + " t0 *= t0;", + " n0 = t0 * t0 * (grad4[gi0] * x0 + grad4[gi0 + 1] * y0 + grad4[gi0 + 2] * z0 + grad4[gi0 + 3] * w0);", + " }", + " let t1 = 0.6 - x1 * x1 - y1 * y1 - z1 * z1 - w1 * w1;", + " if (t1 < 0)", + " n1 = 0.0;", + " else {", + " const gi1 = (perm[ii + i1 + perm[jj + j1 + perm[kk + k1 + perm[ll + l1]]]] % 32) * 4;", + " t1 *= t1;", + " n1 = t1 * t1 * (grad4[gi1] * x1 + grad4[gi1 + 1] * y1 + grad4[gi1 + 2] * z1 + grad4[gi1 + 3] * w1);", + " }", + " let t2 = 0.6 - x2 * x2 - y2 * y2 - z2 * z2 - w2 * w2;", + " if (t2 < 0)", + " n2 = 0.0;", + " else {", + " const gi2 = (perm[ii + i2 + perm[jj + j2 + perm[kk + k2 + perm[ll + l2]]]] % 32) * 4;", + " t2 *= t2;", + " n2 = t2 * t2 * (grad4[gi2] * x2 + grad4[gi2 + 1] * y2 + grad4[gi2 + 2] * z2 + grad4[gi2 + 3] * w2);", + " }", + " let t3 = 0.6 - x3 * x3 - y3 * y3 - z3 * z3 - w3 * w3;", + " if (t3 < 0)", + " n3 = 0.0;", + " else {", + " const gi3 = (perm[ii + i3 + perm[jj + j3 + perm[kk + k3 + perm[ll + l3]]]] % 32) * 4;", + " t3 *= t3;", + " n3 = t3 * t3 * (grad4[gi3] * x3 + grad4[gi3 + 1] * y3 + grad4[gi3 + 2] * z3 + grad4[gi3 + 3] * w3);", + " }", + " let t4 = 0.6 - x4 * x4 - y4 * y4 - z4 * z4 - w4 * w4;", + " if (t4 < 0)", + " n4 = 0.0;", + " else {", + " const gi4 = (perm[ii + 1 + perm[jj + 1 + perm[kk + 1 + perm[ll + 1]]]] % 32) * 4;", + " t4 *= t4;", + " n4 = t4 * t4 * (grad4[gi4] * x4 + grad4[gi4 + 1] * y4 + grad4[gi4 + 2] * z4 + grad4[gi4 + 3] * w4);", + " }", + " // Sum up and scale the result to cover the range [-1,1]", + " return 27.0 * (n0 + n1 + n2 + n3 + n4);", + " };", + "}", + "", + "gdjs._cameraShakeExtension = {", + " noiseManager: new NoiseManager(),", + "};", + "" + ], + "parameterObjects": "", + "useStrict": true, + "eventsSheetExpanded": true + } + ], + "parameters": [], + "objectGroups": [] + }, + { + "description": "Generate a number from 2 dimensional simplex noise.", + "fullName": "2D noise", + "functionType": "Expression", + "name": "Noise2d", + "private": true, + "sentence": "", + "events": [ + { + "type": "BuiltinCommonInstructions::JsCode", + "inlineCode": [ + "const name = eventsFunctionContext.getArgument(\"Name\");\r", + "const x = eventsFunctionContext.getArgument(\"X\");\r", + "const y = eventsFunctionContext.getArgument(\"Y\");\r", + "\r", + "eventsFunctionContext.returnValue = gdjs._cameraShakeExtension.noiseManager.getGenerator(name).noise(x, y);" + ], + "parameterObjects": "", + "useStrict": true, + "eventsSheetExpanded": true + } + ], + "expressionType": { + "type": "expression" + }, + "parameters": [ + { + "description": "Generator name", + "name": "Name", + "type": "string" + }, + { + "description": "X coordinate", + "name": "X", + "type": "expression" + }, + { + "description": "Y coordinate", + "name": "Y", + "type": "expression" + } + ], + "objectGroups": [] + }, + { + "description": "Generate a number from 3 dimensional simplex noise.", + "fullName": "3D noise", + "functionType": "Expression", + "name": "Noise3d", + "private": true, + "sentence": "", + "events": [ + { + "type": "BuiltinCommonInstructions::JsCode", + "inlineCode": [ + "const name = eventsFunctionContext.getArgument(\"Name\");\r", + "const x = eventsFunctionContext.getArgument(\"X\");\r", + "const y = eventsFunctionContext.getArgument(\"Y\");\r", + "const z = eventsFunctionContext.getArgument(\"Z\");\r", + "\r", + "eventsFunctionContext.returnValue = gdjs._cameraShakeExtension.noiseManager.getGenerator(name).noise(x, y, z);" + ], + "parameterObjects": "", + "useStrict": true, + "eventsSheetExpanded": true + } + ], + "expressionType": { + "type": "expression" + }, + "parameters": [ + { + "description": "Generator name", + "name": "Name", + "type": "string" + }, + { + "description": "X coordinate", + "name": "X", + "type": "expression" + }, + { + "description": "Y coordinate", + "name": "Y", + "type": "expression" + }, + { + "description": "Z coordinate", + "name": "Z", + "type": "expression" + } + ], + "objectGroups": [] + }, + { + "description": "Generate a number from 4 dimensional simplex noise.", + "fullName": "4D noise", + "functionType": "Expression", + "name": "Noise4d", + "private": true, + "sentence": "", + "events": [ + { + "type": "BuiltinCommonInstructions::JsCode", + "inlineCode": [ + "const name = eventsFunctionContext.getArgument(\"Name\");\r", + "const x = eventsFunctionContext.getArgument(\"X\");\r", + "const y = eventsFunctionContext.getArgument(\"Y\");\r", + "const z = eventsFunctionContext.getArgument(\"Z\");\r", + "const w = eventsFunctionContext.getArgument(\"W\");\r", + "\r", + "eventsFunctionContext.returnValue = gdjs._cameraShakeExtension.noiseManager.getGenerator(name).noise(x, y, z, w);" + ], + "parameterObjects": "", + "useStrict": true, + "eventsSheetExpanded": true + } + ], + "expressionType": { + "type": "expression" + }, + "parameters": [ + { + "description": "Generator name", + "name": "Name", + "type": "string" + }, + { + "description": "X coordinate", + "name": "X", + "type": "expression" + }, + { + "description": "Y coordinate", + "name": "Y", + "type": "expression" + }, + { + "description": "Z coordinate", + "name": "Z", + "type": "expression" + }, + { + "description": "W coordinate", + "name": "W", + "type": "expression" + } + ], + "objectGroups": [] + }, + { + "description": "Create a noise generator with default settings (frequency = 1, octaves = 1, persistence = 0.5, lacunarity = 2).", + "fullName": "Create a noise generator", + "functionType": "Action", + "name": "Create", + "private": true, + "sentence": "Create a noise generator named _PARAM1_", + "events": [ + { + "type": "BuiltinCommonInstructions::JsCode", + "inlineCode": [ + "const name = eventsFunctionContext.getArgument(\"Name\");\r", + "\r", + "gdjs._cameraShakeExtension.noiseManager.getGenerator(name);" + ], + "parameterObjects": "", + "useStrict": true, + "eventsSheetExpanded": false + } + ], + "parameters": [ + { + "description": "Generator name", + "name": "Name", + "type": "string" + } + ], + "objectGroups": [] + }, + { + "description": "Delete a noise generator and loose its settings.", + "fullName": "Delete a noise generator", + "functionType": "Action", + "name": "Delete", + "private": true, + "sentence": "Delete _PARAM1_ noise generator", + "events": [ + { + "type": "BuiltinCommonInstructions::JsCode", + "inlineCode": [ + "const name = eventsFunctionContext.getArgument(\"Name\");\r", + "\r", + "gdjs._cameraShakeExtension.noiseManager.deleteGenerator(name);" + ], + "parameterObjects": "", + "useStrict": true, + "eventsSheetExpanded": false + } + ], + "parameters": [ + { + "description": "Generator name", + "name": "Name", + "type": "string" + } + ], + "objectGroups": [] + }, + { + "description": "Delete all noise generators and loose their settings.", + "fullName": "Delete all noise generators", + "functionType": "Action", + "name": "DeleteAll", + "private": true, + "sentence": "Delete all noise generators", + "events": [ + { + "type": "BuiltinCommonInstructions::JsCode", + "inlineCode": "gdjs._cameraShakeExtension.noiseManager.deleteAllGenerators();", + "parameterObjects": "", + "useStrict": true, + "eventsSheetExpanded": false + } + ], + "parameters": [], + "objectGroups": [] + }, + { + "description": "The seed is a number used to generate the random noise. Setting the same seed will result in the same random noise generation. It's for example useful to generate the same world, by saving this seed value and reusing it later to generate again a world.", + "fullName": "Noise seed", + "functionType": "Action", + "name": "SetSeed", + "private": true, + "sentence": "Change the noise seed to _PARAM1_", + "events": [ + { + "type": "BuiltinCommonInstructions::JsCode", + "inlineCode": "gdjs._cameraShakeExtension.noiseManager.setSeed(eventsFunctionContext.getArgument(\"Seed\"));", + "parameterObjects": "", + "useStrict": true, + "eventsSheetExpanded": false + } + ], + "parameters": [ + { + "description": "Seed", + "longDescription": "15 digits numbers maximum", + "name": "Seed", + "type": "expression" + } + ], + "objectGroups": [] + }, + { + "description": "Change the looping period on X used for noise generation. The noise will wrap-around on X.", + "fullName": "Noise looping period on X", + "functionType": "Action", + "name": "SetLoopPeriodX", + "private": true, + "sentence": "Change the looping period on X of _PARAM2_: _PARAM1_", + "events": [ + { + "type": "BuiltinCommonInstructions::JsCode", + "inlineCode": [ + "const name = eventsFunctionContext.getArgument(\"Name\");\r", + "\r", + "gdjs._cameraShakeExtension.noiseManager.getGenerator(name).xLoopPeriod = eventsFunctionContext.getArgument(\"LoopPeriod\");" + ], + "parameterObjects": "", + "useStrict": true, + "eventsSheetExpanded": false + } + ], + "parameters": [ + { + "description": "Looping period on X", + "name": "LoopPeriod", + "type": "expression" + }, + { + "description": "Generator name", + "name": "Name", + "type": "string" + } + ], + "objectGroups": [] + }, + { + "description": "Change the looping period on Y used for noise generation. The noise will wrap-around on Y.", + "fullName": "Noise looping period on Y", + "functionType": "Action", + "name": "SetLoopPeriodY", + "private": true, + "sentence": "Change the looping period on Y of _PARAM2_: _PARAM1_", + "events": [ + { + "type": "BuiltinCommonInstructions::JsCode", + "inlineCode": [ + "const name = eventsFunctionContext.getArgument(\"Name\");\r", + "\r", + "gdjs._cameraShakeExtension.noiseManager.getGenerator(name).yLoopPeriod = eventsFunctionContext.getArgument(\"LoopPeriod\");" + ], + "parameterObjects": "", + "useStrict": true, + "eventsSheetExpanded": true + } + ], + "parameters": [ + { + "description": "Looping period on Y", + "name": "LoopPeriod", + "type": "expression" + }, + { + "description": "Generator name", + "name": "Name", + "type": "string" + } + ], + "objectGroups": [] + }, + { + "description": "Change the base frequency used for noise generation. A lower frequency will zoom in the noise.", + "fullName": "Noise base frequency", + "functionType": "Action", + "name": "SetFrequency", + "private": true, + "sentence": "Change the noise frequency of _PARAM2_: _PARAM1_", + "events": [ + { + "type": "BuiltinCommonInstructions::JsCode", + "inlineCode": [ + "const name = eventsFunctionContext.getArgument(\"Name\");\r", + "\r", + "gdjs._cameraShakeExtension.noiseManager.getGenerator(name).frequency = eventsFunctionContext.getArgument(\"Frequency\");" + ], + "parameterObjects": "", + "useStrict": true, + "eventsSheetExpanded": false + } + ], + "parameters": [ + { + "description": "Frequency", + "name": "Frequency", + "type": "expression" + }, + { + "description": "Generator name", + "name": "Name", + "type": "string" + } + ], + "objectGroups": [] + }, + { + "description": "Change the number of octaves used for noise generation. It can be seen as layers of noise with different zoom.", + "fullName": "Noise octaves", + "functionType": "Action", + "name": "SetOctaves", + "private": true, + "sentence": "Change the number of noise octaves of _PARAM2_: _PARAM1_", + "events": [ + { + "type": "BuiltinCommonInstructions::JsCode", + "inlineCode": [ + "const name = eventsFunctionContext.getArgument(\"Name\");\r", + "\r", + "gdjs._cameraShakeExtension.noiseManager.getGenerator(name).octaves = eventsFunctionContext.getArgument(\"Octaves\");" + ], + "parameterObjects": "", + "useStrict": true, + "eventsSheetExpanded": false + } + ], + "parameters": [ + { + "description": "Octaves", + "name": "Octaves", + "type": "expression" + }, + { + "description": "Generator name", + "name": "Name", + "type": "string" + } + ], + "objectGroups": [] + }, + { + "description": "Change the persistence used for noise generation. At its default value \"0.5\", it halves the noise amplitude at each octave.", + "fullName": "Noise persistence", + "functionType": "Action", + "name": "SetPersistence", + "private": true, + "sentence": "Change the noise persistence of _PARAM2_: _PARAM1_", + "events": [ + { + "type": "BuiltinCommonInstructions::JsCode", + "inlineCode": [ + "const name = eventsFunctionContext.getArgument(\"Name\");\r", + "\r", + "gdjs._cameraShakeExtension.noiseManager.getGenerator(name).persistence = eventsFunctionContext.getArgument(\"Persistence\");" + ], + "parameterObjects": "", + "useStrict": true, + "eventsSheetExpanded": false + } + ], + "parameters": [ + { + "description": "Persistence", + "name": "Persistence", + "type": "expression" + }, + { + "description": "Generator name", + "name": "Name", + "type": "string" + } + ], + "objectGroups": [] + }, + { + "description": "Change the lacunarity used for noise generation. At its default value \"2\", it doubles the frequency at each octave.", + "fullName": "Noise lacunarity", + "functionType": "Action", + "name": "SetLacunarity", + "private": true, + "sentence": "Change the noise lacunarity of _PARAM2_: _PARAM1_", + "events": [ + { + "type": "BuiltinCommonInstructions::JsCode", + "inlineCode": [ + "const name = eventsFunctionContext.getArgument(\"Name\");\r", + "\r", + "gdjs._cameraShakeExtension.noiseManager.getGenerator(name).lacunarity = eventsFunctionContext.getArgument(\"Lacunarity\");" + ], + "parameterObjects": "", + "useStrict": true, + "eventsSheetExpanded": false + } + ], + "parameters": [ + { + "description": "Lacunarity", + "name": "Lacunarity", + "type": "expression" + }, + { + "description": "Generator name", + "name": "Name", + "type": "string" + } + ], + "objectGroups": [] + }, + { + "description": "The seed used for noise generation.", + "fullName": "Noise seed", + "functionType": "Expression", + "name": "Seed", + "private": true, + "sentence": "", + "events": [ + { + "type": "BuiltinCommonInstructions::JsCode", + "inlineCode": "eventsFunctionContext.returnValue = gdjs._cameraShakeExtension.noiseManager.seed;", + "parameterObjects": "", + "useStrict": true, + "eventsSheetExpanded": false + } + ], + "expressionType": { + "type": "expression" + }, + "parameters": [], + "objectGroups": [] + }, + { + "description": "The base frequency used for noise generation.", + "fullName": "Noise base frequency", + "functionType": "Expression", + "name": "Frequency", + "private": true, + "sentence": "", + "events": [ + { + "type": "BuiltinCommonInstructions::JsCode", + "inlineCode": [ + "const name = eventsFunctionContext.getArgument(\"Name\");\r", + "\r", + "eventsFunctionContext.returnValue = gdjs._cameraShakeExtension.noiseManager.getGenerator(name).frequency;" + ], + "parameterObjects": "", + "useStrict": true, + "eventsSheetExpanded": false + } + ], + "expressionType": { + "type": "expression" + }, + "parameters": [ + { + "description": "Generator name", + "name": "Name", + "type": "string" + } + ], + "objectGroups": [] + }, + { + "description": "The number of octaves used for noise generation.", + "fullName": "Noise octaves number", + "functionType": "Expression", + "name": "Octaves", + "private": true, + "sentence": "", + "events": [ + { + "type": "BuiltinCommonInstructions::JsCode", + "inlineCode": [ + "const name = eventsFunctionContext.getArgument(\"Name\");\r", + "\r", + "eventsFunctionContext.returnValue = gdjs._cameraShakeExtension.noiseManager.getGenerator(name).octaves;" + ], + "parameterObjects": "", + "useStrict": true, + "eventsSheetExpanded": false + } + ], + "expressionType": { + "type": "expression" + }, + "parameters": [ + { + "description": "Generator name", + "name": "Name", + "type": "string" + } + ], + "objectGroups": [] + }, + { + "description": "The persistence used for noise generation.", + "fullName": "Noise persistence", + "functionType": "Expression", + "name": "Persistence", + "private": true, + "sentence": "", + "events": [ + { + "type": "BuiltinCommonInstructions::JsCode", + "inlineCode": [ + "const name = eventsFunctionContext.getArgument(\"Name\");\r", + "\r", + "eventsFunctionContext.returnValue = gdjs._cameraShakeExtension.noiseManager.getGenerator(name).persistence;" + ], + "parameterObjects": "", + "useStrict": true, + "eventsSheetExpanded": false + } + ], + "expressionType": { + "type": "expression" + }, + "parameters": [ + { + "description": "Generator name", + "name": "Name", + "type": "string" + } + ], + "objectGroups": [] + }, + { + "description": "The lacunarity used for noise generation.", + "fullName": "Noise lacunarity", + "functionType": "Expression", + "name": "Lacunarity", + "private": true, + "sentence": "", + "events": [ + { + "type": "BuiltinCommonInstructions::JsCode", + "inlineCode": [ + "const name = eventsFunctionContext.getArgument(\"Name\");\r", + "\r", + "eventsFunctionContext.returnValue = gdjs._cameraShakeExtension.noiseManager.getGenerator(name).lacunarity;" + ], + "parameterObjects": "", + "useStrict": true, + "eventsSheetExpanded": false + } + ], + "expressionType": { + "type": "expression" + }, + "parameters": [ + { + "description": "Generator name", + "name": "Name", + "type": "string" + } + ], + "objectGroups": [] + } + ], + "eventsFunctionsFolderStructure": { + "folderName": "__ROOT", + "children": [ + { + "functionName": "onSceneLoaded" + }, + { + "functionName": "onScenePostEvents" + }, + { + "functionName": "onScenePreEvents" + }, + { + "functionName": "ShakeCamera" + }, + { + "functionName": "CameraShake" + }, + { + "functionName": "StartShaking" + }, + { + "functionName": "StopShaking" + }, + { + "folderName": "Camera shake configuration", + "children": [ + { + "functionName": "SetLayerShakable" + }, + { + "functionName": "SetLayerTranslationAmplitude" + }, + { + "functionName": "SetLayerRotationAmplitude" + }, + { + "functionName": "SetLayerZoomAmplitude" + }, + { + "functionName": "SetLayerShakingFrequency" + }, + { + "functionName": "SetDefaultTranslationAmplitude" + }, + { + "functionName": "SetDefaultRotationAmplitude" + }, + { + "functionName": "SetDefaultZoomAmplitude" + }, + { + "functionName": "SetDefaultShakingFrequency" + } + ] + }, + { + "functionName": "IsShaking" + }, + { + "functionName": "onFirstSceneLoaded" + }, + { + "functionName": "Noise2d" + }, + { + "functionName": "Noise3d" + }, + { + "functionName": "Noise4d" + }, + { + "functionName": "Create" + }, + { + "functionName": "Delete" + }, + { + "functionName": "DeleteAll" + }, + { + "functionName": "SetSeed" + }, + { + "functionName": "SetLoopPeriodX" + }, + { + "functionName": "SetLoopPeriodY" + }, + { + "functionName": "SetFrequency" + }, + { + "functionName": "SetOctaves" + }, + { + "functionName": "SetPersistence" + }, + { + "functionName": "SetLacunarity" + }, + { + "functionName": "Seed" + }, + { + "functionName": "Frequency" + }, + { + "functionName": "Octaves" + }, + { + "functionName": "Persistence" + }, + { + "functionName": "Lacunarity" + } + ] + }, + "eventsBasedBehaviors": [], + "eventsBasedObjects": [] } ], "externalLayouts": [] diff --git a/examples/starting-3d-flight-sim/assets/Flat dark joystick border vertical.png b/examples/starting-3d-flight-sim/assets/Flat dark joystick border vertical.png index d468a61805f59debaa6701a0148db543fc9dc41a..ec315d4b6b8b57c6528ac9b1f3995a45acf69ad5 100644 GIT binary patch delta 3904 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 delta 1615 zcmV-V2C(_39pDU*B!3BTNLh0L01FZT01FZU(%pXi000IONkl$95AT zsS59mS44;|gnx(N+J#8cGO*RzEFP~zospvV09F@@5#H?{@q=XL22*Ds#!`x*5VwTo z4s|@d9%0$U>nUU)7Q7)|FM+)wU6p^Om0hR{xm+$Q0v6(ikQ?aY<#~Q6%d$d$CM1W~ zyWPhizDPv)k<3iubUOVV^8QI7_f&ihfrsPx+j*f+tbbe*UkoCwg&3a*Lr*ruVzFoy zUqm9@#3sJr>w85}TrEIx+~)>gFGPHR2>TfQ+$co2A>jZL6U2uIsq=v;-uI97Tf#!c zjkg1?`t4wW(l~eGoe1k|FBqpvXt>pz0R}xt%4c&^K9fwX4@^h5 zhA#J6GvxxN40aI`y1hS ze!&%-b_4%zQ{cc6n4Tf#%mZu^VFU=K0%QaW3=gfpBS1iakgx!G5nx&i9P-2F^Eody za({+iKu&O?c?w*XhG)!&Cg zfawTOXzfh}xLhv3JP-m*OMpTeFcIK);zNMx39zpSvp$9g3nr zfC#WcT9783hTwA|M1W}tu#fn9Cn4X75Pt!tBf!4ms|47$7kny01ek^ZhY;Uj!w8>6 zhyddk;1J>)d>Td%qz4@0!NA)Q`a##Y5+IinffdyyMU3Qw=}11;@`9lUeB)Rq*dEK| zW_&OlZvDyC9AGz&&+VvSn1Fn6JsVgGaLWaiV4DGRMK73Uz@Lqm0wxIvi3pe`Ab)&O z4Vd(R`Km7PX8*^R)qv^#k9r_D-2d(P!fNoN{&CV;a6gbQJqatw?@HG=xAlWdCg|wAjV`RfH>hw#SQIrNJ#pg@LH)z6u$;Y3pt1%-8PLq%Cl9A&N?sF9;)t-u>03mkWhy=6?j?_NzDg8eR}?-TU?Se(VLoY37R;1oO<7?g%*V zdUWWH@ZG{NT+g?wF|Z1wmtB|_gj>-zEpuT^O@q_WODG2O&`T%==UFe4)8M4t)~7(t z;3}uwOl>lm+|wnY*K2C(`>`l#B^`1+nNP;R0`)#0M7U*vm~t^S`o$ERO@FN(-J(;1 z_z)o^D87>#A>lauQ*FPqkd4HIgaZ*my5d8Ghz}70;``jT3$b^+TCEOcSzd+yZiu~I zv#}}$7~0A6{AvLfMG>0U`|xJ4fLh_%q035G*N9Jlml|If9m<>K|aWyu_P!t1d&3|x5Z z^#%&djQe*yv*GR7eBdP;T6rzP(=RLGcKmF4OKAR@U`TkaUxkVd>k&09nX(WY;h_fm z(7aCR2!JNd+2eUyzL8gq@wyPgY+$C*BJCAJ7NfknL`Nwlg2YAF$w>KrNUF|acY0Pb z+4w{CTkRp=E%$PDBDE&8BFg(ZY;T0bA^K$5`q@vPQ+=-f4MOTa`w!ht=DUf6Hmd*t N002ovPDHLkV1i6p4Cnv= diff --git a/examples/starting-3d-flight-sim/assets/Flat dark joystick thumb.png b/examples/starting-3d-flight-sim/assets/Flat dark joystick thumb.png index 36f5262a9055b15bf36ba5ddee957e1fa854561e..8acf179f8660be36ccea1198105da35c07154fd7 100644 GIT binary patch delta 2913 zcmV-n3!e0p2>TY0Bo78+OGiWi{{a60|De66laV18e*gz`Nliru>IW485iPkW{Hp)} z3kXR>K~#9!?Oj=H8`l**_stBok)rm+alOTHY)jTcOO_Pdau+1&5)^3yq%HEUA680M5oiMuZ2}=5KmkC$ zh2P)_Fvb}e2lHhHUl&5_kWyZ;W~Ms(dppL`v7MAICsJA4SY{Z;E@u8VY2tALtxxO` z_=1w^BNFrbCIX(RoBGKF1b<(^q1*oatr!n)hnx?%3qKDGS z{^p92xlEA9m_UsOu{x~uKjTlcg8Rl6VglBVki zf9I^`o&z&?O56HT&*_$9_mluzwpA7Q^Zmad63^p405iWiIehDdi_U#oq=U0J$D9~)svkkj~-4-0jlbobOG^Oq3O>9(2~|=iBMEr zioAlth3t%tVq|y-%$!USq*ChjnaI?Of9Lv6&!&w4J1T05vV6H80<=5Xd8MeN6!`^3 zi*6eq8^Or!p`<$!z~`qXLi^73caN_2$Hb~5KhRj2<@5IeRF`b>HBEy*uVBgT{=5Qc znwD$<2%zoRxp}=+b&ZuN6`;C)U$gGXI0B%=iDW1=4MHq^%0dWen(kC?F4y&pf1}lP z`x;XsKyAbRZ)uwLK7jnRs#w5I9xhMQwL^i%<|iB#Akf(SD3SOl0H2esm9~UZYUxW^ zr66tTwB|m5{#Dc1{FoyGRPWha3DCO$49CX`l*Gu$(&J_$!$VL?rlRBN0`yLG{l4m? zRzcPE`!;Ku)(OC$N}KT)6r!lGe;8se;YuknI&vGK@stnwhomiQyW5WqCR~8+I|6yx ze%}Cq;&kwOAq0e`f#w8=W=#%bWM(?Dy}!GCG@%xhmFqo}F64ja_qJuhvaEC>9{`Fx zS=sN#U0TOg@$A|E3JLKnT*PTBw6S#E2Hd)S^>FNcW1yyP?>?dHhZAwpf40V;Y-#W9 zX*=G!Vhi#E9@4ZwyX3F%x>0CvZ`t;6)`|squ%Y=kMD(D`7 zcY1xf-T3rIxr>#mhl-j#6t@7H$MZN5 zebMDEVOF$PDz1;qf+}kpe|3+?(+Q$Vm%0RkxzDn!n%<5UJ8BOQLNtJ=!euPs-C!qx z#;6vwqavW`y8af@Z_dtrA%SifuWhdgXiEy9XJl*u(H57hq~(G@me*Unv;ew(5P-*} zDrqp7Xs{p(%Pf0~&CK7p|zqdWKpa=_vcm3 zTaZ%xWmf=aZHQTZWnKUww6D9II154AH}4RD8eB>o?gB#W0U&5spt0WYWSnv-aUP~= z286B~?$AT3E{k=2f4cy|9d=F;7}zM7d9zDJsubXW0BF;)b#svzZiv?DL=@NDe#7HI zeqk|uzFgRrh2Z!oCMH7Q^qQ6Cd^JM^e_jFd3JRfV8Y1CIj1CXMvaEQ%TSB!B&9{jt z>XbvTFBfIy4}dr;df$nnNnzd}aL(NDt@{RWw?8_8Z8nx+K|!!V3haW_8{5e=~xKwFjtqO~`UM?|n~ zYgOERDgblTe_x4&C!@~4kAx?&RuPFrmfWV4!sJ9KRzI=?12fTfky2vl=JiEIFwI#E z58Yg|0^GiJ1Lh)8u9U*i&1*5OnHfPHzz|}rb_`EWU}k0-Ma8Aa@%kX8L~v{rq2Sn> z^#41{&8wF`MsBVjdHIFV4Fi*t6Br*Ij`!IWA)T3Te^VUK@v$unw{BdUyV`T_-P|cc zC^(K#aD3U^F!PWA^NnSuAD8t+al?E?5K)^;T*^wRLBYT^mx+|&jIb^9oXbOMU|ZHD z0)Rl{0e6f!1^K(9^{|+mmZID|;4qX@r{;$ul$0O2lsGQHJM#jNCO&|BtIJXQAwehR z1sES2f9nBYy1Y0iz;r0wJ1@Z1i~UndslzTW&Ol1}!NB>hiFpA4KoI@RwIOGsUkLH@ zdEP(!V=5dT1Tf|DlXOhYMy4*`CBV79j-ZtGKU{v2UH!=a_Md4VyGsB75JGzyfaTJZ zG?=!OKVHE3o&N-b!C_|Zba_ga`}3tpIC5i2e*p&1_eje$UvQIga_Bcr^ZSG6d*qS= z0Dx4gg_+N~d?h;gxu2D4`TXa132?Uilsz4ZJm>a;#KN-77yG(T*)atG00LSUQ_IB) zqhG1RmO1m`5?n8iZ54sdUZ3v*lK3uwwQ}Jbo(ydr=BCp8ZFJ5c^$ffBixVoPQ)TG5)o9_g>aiu3dFaTMXUP2_QF> zP0TAOKtW;2{0bB$B}PYw5gbc-e1t5^4D_CExfFLGE$7#J+gmP4DGvY$r;Pmk0u+~& zFIfH~G!4ZiWys4fNG17U+qU;Fhx}y=007->$GVhKPXVx+)%siXhnPepLFCun*cud&h{7a*)yJ=Jyc=%Ez_)k=@rdFtqU zwrxKK=8#h@rF5H_%^Z|cKGAjZ=zA;f)vGyvPy6u}(=wZxdB}S@jg^wpDe{L+ z%RJECc5GF%NmgCIo#|-p2?fWte>rgvK}v~WaD2(_!SOLj2Lxc|j>(DOLuWc#dsc0$ zi8!J0(c7~{W$XS3<{}|P07-oK-^^45qzPnYWiLF3u~CdgdB<3yV!*cTHW$G`LoZ*uIxw90!Xk;N3_Q}H>)M+_h^l*XOh>6MY1=>S?YPI@ zDSy`x#_pyc=(_O&-6KzCXXZv-R)EFRdNIuDT|TB|m3)JU=C(O-R4xmdwl_ zo9666A+$gBo<8A7J?l6Lf7>f+GlXue_he-JWv*h%N+sz4W|&ncKu=DECnwJLbSnvFKw%dgeWJX z4MfNTD36GIpEK|XfN^FHGV>Jz`bdZY%d#(;Gm#4e{oOaxv7P@1TUu73{ky})00000 LNkvXXu0mjflp>}T delta 1022 zcmV zyp+Nh;58IdxZlj$Jks-6fY(q+p8T89>-ApBhkP54$Db&`{}j15xktIz)J8ls@b?s9 z9{9mv@Ykm^RWMB-@(kvH@AvzEBx@c2^>wO{Y^$C3K^KuW9oai^UE2XIUX&sFaK*sK=W{nW8pTH)DjRkD8u6f%9&P z_H?`!95P40;#H&q_SjPoLek}gJY{|;sVM+6CpkADuH4L&VMeSxd7EYkV44!m zkn%)5PST5_fSl7*(dLKI8AmKX*IAg4U?|_FW7UTT$X?><>&pI$T~ttxsnfmA7bqlI;u=xnoS&PUnpeW$T$GwvlA5AW zo>`Ki;O^-g5Z=fq4pem6)5S5Q;?~<+zFdb4M2>tsnLVK{U{}GzzwgyN9FGRARhsqu zny^aupXW@%b+fmhXn9=o^~Mao-=3DmR`;h&@}A`NZFb~^8{1FK&3qvu9sKg&gLmt? zl2_RPO~wbVOtOd<>vdkf?{2E@y|{ec@4jMAH~*bC&y3vu=F{uPE2qRP{p~yHjMF#v zCHwtfPWtc}*5j~)%+YxJC0e?CWdeaG0_N&;baChf9aW0Z$ZZXi_0jv=q{i#n)xG#ZRKbVj zjjtX&TC=)&wE{<7!wzo0F1?^f|0g+hOYryo{`l_R<9D(Tazkb${W#9(YWg+AMlGdy zwbKJpTM0Lgx-(s8E(pCm$`f<^x~)yk-tJF2-;3+}Cd@IHcp%vEe8#fbN2HHjp1tf? z<$Q}XUWDC7hOjdifNcU$(6*udHzdidj*}vRp+MDf{zqWRc z9fRbnte+k)2`u*>=2^U%UOEGhMUrs?@jV_u`e6woS>i zzxegW_ZvZ6K;^snGw#=bw_8yAoe{{m zpA0nV_d8~zkKr5C9U3?USUMOSiJ^L=7yPdHwwvvR-8`U4Z+G(p8Gq$~CfzERpU}C# ziPWF~xy97s`^PsB&jA_VZR|A6^>bP0l+XkK=p&wV diff --git a/examples/starting-3d-flight-sim/starting-3d-flight-sim.json b/examples/starting-3d-flight-sim/starting-3d-flight-sim.json index 68b5a7dd2..2d3041422 100644 --- a/examples/starting-3d-flight-sim/starting-3d-flight-sim.json +++ b/examples/starting-3d-flight-sim/starting-3d-flight-sim.json @@ -1,7 +1,7 @@ { "firstLayout": "", "gdVersion": { - "build": 272, + "build": 274, "major": 5, "minor": 6, "revision": 0 diff --git a/examples/starting-3d-point-and-click-adventure/assets/Border.png b/examples/starting-3d-point-and-click-adventure/assets/Border.png index bed7c2d72a206a4946cdcebd4e3972082bcaf0cc..fc209e345f8331ae85efa24563e4f9342c0bcccf 100644 GIT binary patch delta 1119 zcmaE--@`dUS%85p$=lt9;Xep2*t>l4WJ6K8`d92?qUI`G8tQT^0DN%v6+ugrhKtm`aQ1lS?#&MH*)(9aa2sr z@>Gy>xocSO;K``UprbyI(tGF9itEcK*!j4PT{7B1Xc^~C&j zofc!<(kP)qIinvYN{fCMY9EsPmhd&~t@YCt@eFqo`jU%%JJ;yyPG^66eXV#W>zaG( z);iCWXM8dvF{>zGg-)#Q>ThAEw{u18jg4kM!++*&{BdjlruyQt+m@?piUNhEo&J0I z_QYRL4wTNZPn;C1ec0g0@w@yte(&gRZf5McCOmbC(7`JqVwb0<6=Xg$GMedgsXBeZ z;up_89;`7s9e6Hzvcew&o{RTiCVfBryHhW9u_@c{YKb@R6<_2gKl>#5gxhsD2lI8H zItFcZ?FTRB?a%zZ{QW)F`WNrNsI9x@9x3|skW5HH_j}=O{wj`L zw|VYw(rS8V_RI0S)i;acr=hFY$yiu#JH@~2woe7iokx30RbQ-Jp&)T$R_*!sh8G22 zzW*Z7miT8ujGrveMzwaI`Bfh;Zbd+ z>qlkkg6yV$|J$IsOhwO`d5X%rquKMX&YyLIhc~8kwoGfi;lU|4j6`npb}TtOZK1)U z|6*JV7`PNf92i@eG#UaJSOtK5Cq@wmjRqzT)&PcuL`&8a()vT5)8o#p&Yro6YZ%l& zOA7mR>N+W0^NxwIk7rIBSEI%P<2OI2?m5j8EHr%~lhvZFb*3ei9SWyI4a{!d zF>z^o|0(Kqc+!HnV}Csg69v60BBexCn;soXdLzO7wLWOVRsWpj75fa1ySS^w&)9rl zY4fknAH8l1c>e4t+`L&&wMSy#+U|KqAO5-lb5KW5Rl?uEa=yI6dABE~-Y)F^d}8;v zB-?X;mdyWfefz$kZY zyW90A2!7DII?2JNV^?y+B&#>BHZSM7F0sAZ=F+$QzAr` zR46K0+q9@~C}|IU6{jqH-GKr1q($24wuIUOXNZ}m@VUSKp@$}YHC2% zfThM%kbdPXZI8IY5kgP|y#0vH>tnA{hfXHk9*f_)(jagX$sl6%;;!roRd)@az{e%N zL_(6EZ(hRU_su@zn7^yaCQ`&|pZea{PJr@k@((m`aa6rjx}rzlnB27FqR+7@yK=;@uNA#pTe=Cl-Rnf(0OFFIfi4yyM z4l~ySP2ZSoMx$kZbHTFDNE|DisJ?QT!yLV`t~+Y z4VeYRuTA~Cu|*^>8LMqb-teGc7fReYn)KH=3}1KcgVw#8zKyP3S2gr7bJsvE3TyA4 zNy8Wwlo#Elb04F2YUs<4%s#=$oxgt3g^Ae8eY$Fr(v(YTm=d_O+qGGD0omOUOFaB| z=uS?Nce3g(hs*gI{_cJw51k{4u--1+v8pC%O%SDnphC1-xuL;qg;TGW3ANqDbXJ)( zfWC>y;n`l!skFJ$;6Xj%BVYV?#7;tDm)e{MB|KAK+Wm;aVB?+an6vRo+N>&fjab6k z*>If#j_qeTl8i;u>FJmDS-Y?8usyWloG{apL?u>GeM@jppCE%;aiMnSLhc+Z$=|SU z|2>yw5UwaSZGV<;NYf$x-)(YFX%|-e?1y3^$Q#2Rt}b3OberA*E=TL$_YWE52=Eu(aYfIwaG;>48RwmJjTqB*JK$g%VI<7ZM#cYq_*&rx1h zSWC0d7q-=>oXg{B=A zb=-Z!|bcRlX;8K?3$X|(k%@C{1>xK5Rst4sC>pBHvlA zf<<{*`7!2(2hm%wGwQ372d47vG|Dh z{-?R2g{&gq$_n|?KG#Fr`i){@D5d|&j5~=+y3W`4JGZncH#27oGra6*$Lci}p&U|9 z?}g~jMVYho%k46{?^j%!u4}(m7n~Ixf?9s^VXb-JYtIw;4{ZzW(~F%3TNXS0*^_&# zzh;$NZR7ZuW9XmmC+_U6?^nC#dYrGSziRz*-R0PkoA2(Eq$YFJsWwlJTbxy0>V@}* zR`5LWcV5zDJC4kBgO81C>Jt)Z-7`H!JI4sT+w*AmkDk;noLT8|Q=N7cTlSaXg#O3$ zxQW<~8e&iA9e8b5p_TvAi!Pc)kq3;tu3MPlAZiDmBo>l{&YQ*J!>6B()-Mb}Eb0=n z&(b<^qtnLpE!YmPF3Fzy=j!Hd1czmFA9xKv@?XaIqpQ>ZppJ0tnt}C9Ra;9j2@@tqx9GNnpUA8SfoAOCy1IIkTwTBJB*6Z0Xh*7}$7OH5ypU0*xgIc1|G5i8 zj|L_-WdzClCdBV;#G56Y^tq4{zTr__(}vPJ)MBscOSH`aB=O;pSzB#Q zqs@seg?WkUwnT212O>3pG-uzg`)@5;m!%hLtV){gm**my?B6vKd4@v|=_IRPjT&0m zJ-ukQO6|(Zun2` zNk5l7P~DMR*ukjifTMzIhXY<2pU_$v%cDIP+TqgB;)-zpy`}aOAmZm5# zKQ6i**zp%}ft{U7UV~=}_;z%bfWfwt@rA(74+0TZ$b@ueC|d$%u!Fc^j*#Knix4oE z@bPh_Yf@RH-BNch^H)v#e}hWY(8Kr2BIQ<2-Ob-iU12gOZ-J-DOJd?5!=BWZ%h$%=%F4GTCBf%k zb|<-#cK;`8;L70X#6&q>k|$m{xu(;vQ`6}N1Ik@$i2BW{!9hbc&7kMa!h`zhEjefH zS6Y2KSUa6xNv|?#be;}3lSR&)_s1mRMy7duC;wz`*uD!3MhIRzo7Zd45^bu%Z>OB! znPM&#G)`@} zE%jyP^5w~Ncbl)j8eabz)6!e>TlYY4r}c^or&RN~SB(m;z9?r$ z_IcG3Jbp=PA1f9v z$E;E|wb@DL2QCYbw?#bVJ?}BD@6O%+sa=EWe)XM` z!J`k^Rs$0+d&kP9h95p1`px9xRr=qQtWy3cVSSqU>Kd!=p6TY8SJLgXooU@}0cnq0 za#TqVxBPCAmRp<*=?cI5CIa{)x;0*PRy$pb5*cXlbdLIb__HPF{zp^)`H8%;0-^oZ zb<^2~-Ul^NBnrpXOgcqCayW}6NmG%=vB_z%gDQ><&swFCwp>HQQOWa57acHV0&bw! R??7*YNN#J1r(9?;{{>}*dx8J} diff --git a/examples/starting-3d-point-and-click-adventure/assets/Border2.png b/examples/starting-3d-point-and-click-adventure/assets/Border2.png index e9d7f4a4deabf45ec5b84bb81f868b43a0c0dd8d..5d5ab796adbfc84cbf336b7f52adc93b341467c0 100644 GIT binary patch literal 1031 zcmeAS@N?(olHy`uVBq!ia0vp^2_VeD1|%QND7OGoY)RhkE)4%caKYZ?lYt_f1s;*b z3=G^tAk28_ZrvZCAbW|YuPgg2b}>;)eddSL^BEYJr+B(JhE&{oJ9l^3l>m{p|Ftf9hl~Xq+Np05s z(DZ1|oS6&s#Q$G^rgHTE+s)@^W^PO1`E#MXAYy#kJ#d&w{orR>xMi`Aiy{ zO;-Mi|ITgKHjzExWn;yXRkbF+G`uHon%uBwgP_YbMmBrNTkWqlWxLOq_%Q#5s>J5A zAFOj*<$7+OGkOmBCW(Ldrz-5vw0`aLivN6>$( zJTzpiOjMTG{I+`Sn%7eK-{Kyhy(ee4r@sARLB*;fo+gIp`Y}hQzRZl7xv$IM%bs5D zER(Za^VvW1GtOq_&UhpIXTg>mcW@6}3+&7Svl z{)V?cH~N6|j(*0YYXl9A*Y&>%7ri`Z!rxUzV(U9*8Va~97TZ(xagJu$k(mn>jMmA= zg?^pJ3F0W3teb9sb^eS5_gh9L@1!rdtKFEmjfYqH*Y*Gg)&Pcu3|tB#4vZ~K8Vx|c z0M`NrCq@wmjRqzTs!1NGS5e?OZ@92;OX>@e0yCS_EY_E%Eo@A9vS6~tt}`swkIn=H zNNcPLJiq?;nZrtcb0oF27Te%%&mgl~;yf8T@_Q2t}=c>Oh>{iS&ng7Cn zq%QgCU+vV=o*!tmz%PC(g+@&1%B~C9mw&uq? z&LbB;+sm*26UKO-KW$>ELzPd_qD}9eOMl*OwBFXBtdt~C$*`1T|ElYDQngc-dfwdU zcuYY*hee@nqN|2dhs*MPuAEsb!URGxzGyaF3jMhM6YHVac=c;G7QcX5mci52&t;uc GLK6UZuEA>n literal 5983 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 literal 0 HcmV?d00001 diff --git a/examples/starting-3d-point-and-click-adventure/assets/StartingCollisionArea.png b/examples/starting-3d-point-and-click-adventure/assets/StartingCollisionArea.png index 0cf1020205023234d56757be2c4eed0e05d7002d..443292ad3a56caa45af6177c5c49bc749794facf 100644 GIT binary patch delta 753 zcmV|gm<#igRwFyugrlh>ilg7#fNLFgUk6GgZNLK0~ zJBHCCAX%w{E?C9_kZhGP22m-sFPiYXZ7s|5%ERJZjLM041+}XuyfhQl&Kkag_&CCqPo3 zqft(r0BL!SMmcr@{{Og$6V66CG5~3L&PF*DfD7`R3)a9LkTiMF3m}Q|pc_C^}5GzPi00000NkvXXu0mjfQ3_BV delta 817 zcmV-11J3-22FC`FBYy#fX+uL$Nkc;*aB^>EX>4Tx04R}tkvmAkP!xv$rb<;R3U&~2 z$WWau_(B||ibb$c+6t{YnB4RQO&XFE7e~Rh;NWAi>fqw6tAnc`2tGjE1t&!pDe>H- z&?3eIm;3SG^Z(p?E+Ev)Of@^k0ade%WGpIXva4d?72Ozs4}blbkeI2*Q;TVM*4I6C zQr$&(mUrKu)vM%82KYqcIi?#H@dokCrloV!6?xAV%r}h zKwuYW)NK3v*tQ!dfd3h|(pvsX9hm(jz1GsgM?l{;aBU!s02y>eSad^gZEa<4bO1wgWnpw>WFU8GbZ8()Nlj2!fese{00ACJL_t(| z+U?pw4u66$6olciVdKR(mk06$p3Q|j3<0DqZGG>}uyRMfKN2*Z+w+dcJYo3tdVfFv z^Ztn$4*-c7KTm@LSi=wf23W)o3t$yLB!Fdn_W;)MT?2T)cMISJ-z9)2eDeU_@J$1F z#5W7z72hO)XZ+;=-tm_LM8H=Eh=Q*Q5D8x#Ab%RZC_qGfc7Uk(tN@Ymrvakle+FQ{ z9|mB-?*?GPZvwF4R{Go>JeZ)B~HwEK>Gmg1GEod#t#dieSqOtt@Z)T z_-+BT4{-GJTKfQIe6s-B2RQyqt$hG9zB+*R0YUJ^0kjVYhR+V5eLztBX#nj5g5wVZ vXeu8-g5LztK7b6r1kgT!6u)`|SWk&R!a@PWh3-$;00000NkvXXu0mjfQ@3cv diff --git a/examples/starting-3d-point-and-click-adventure/assets/StartingTiledGround.png b/examples/starting-3d-point-and-click-adventure/assets/StartingTiledGround.png index 08fb856af5bce51a979df5406f58ffce2a434a2a..380baea6566db151e41f861a43578cc7cbc510e4 100644 GIT binary patch literal 363 zcmeAS@N?(olHy`uVBq!ia0vp^4j|0I1|(Ny7TyC=Y)RhkE)4%caKYZ?lYt_f1s;*b z3=De8Ak0{?)V>TT$X?><>&pI$T~ttxsnfmA7bqlI;u=xnoS&PUnpeW$T$GwvlA5AW zo>`Ki;O^-g5Z=fq4pem6)5S5Q;?~<+zFdb4M2>tsnLVK{U{}GzzwgyN9FGRARhsqu zny^aupXW@%b+fmhXn9=o^~Mao-=3DmR`;h&@}A`NZFb~^8{1FK&3qvu9sKg&gLmt? zl2_RPO~wbVOtOd<>vdkf?{2E@y|{ec@4jMAH~*bC&y3vu=F{uPE2qRP{p~yHjMF#v zCHwtfPWtc}*5j~)%+YxJC0e?CWdeaG0_N&;baChf9aW0Z$ZZXi_0jv=q{i#n)xG#ZRKbVj zjjtX&TC=)&wE{<7!wzo0F1?^f|0g+hOYryo{`l_R<9D(Tazkb${W#9(YWg+AMlGdy zwbKJpTM0Lgx-(s8E(pCm$`f<^x~)yk-tJF2-;3+}Cd@IHcp%vEe8#fbN2HHjp1tf? z<$Q}XUWDC7hOjdifNcU$(6*udHzdidj*}vRp+MDf{zqWRc z9fRbnte+k)2`u*>=2^U%UOEGhMUrs?@jV_u`e6woS>i zzxegW_ZvZ6K;^snGw#=bw_8yAoe{{m zpA0nV_d8~zkKr5C9U3?USUMOSiJ^L=7yPdHwwvvR-8`U4Z+G(p8Gq$~CfzERpU}C# ziPWF~xy97s`^PsB&jA_VZR|A6^>bP0l+XkK=p&wV diff --git a/examples/starting-3d-point-and-click-adventure/assets/StartingWall.png b/examples/starting-3d-point-and-click-adventure/assets/StartingWall.png index dc2b30525059ad9f707e1900900dd164e4cb191e..7cec4140fc9c15502023101b0cd5ed9ca3caf202 100644 GIT binary patch literal 1720 zcmd6o|5wsi7{@Q5l6piokKd-I*0Hm*spA~Y)>O!E85@#ETS`;>vZKvXa3-l!5o4Qn z+R8G+@61l=QiD^jaG0pMV=Xk#%w~n~QOPm%An0d^$hX1TPyGe{aL;p}=iXoL^M2m1 zdp?<*=;HLA69B-4cqBdr0EBgm0J|KlA*1Byd~0wlK0-MIfU}!zAwX-pm+i`#l*CxT zd+MvSCUz%dl41b3bIIA5V{gqdr3vY!sr3BPvYe7UaJb-PQBLQbYhME35JrrTIaZEP zy^y9XrTcup=#C@BT~*F>DCMItVQ+}f>@P-UsOU<|S54>!g)ky`L#WS})`s^zH{Al4 zc}x@BcjE<3u&mUj9`wazndd@;GmOj@a?f{P_Zgrl`lpaC%bcdOz-n$@L|cQ}LYOi#xx&@wQXk`5 zNKbM_UXx6tHqOW{y|A{Sf4~q=&yZT_d~d-vcCae+Q1A{{paWGL<>!2m{OFlr?gz>G zmb!v~URYkf+0Zp%o|bT}Dpb1G1~4H1r-*VU6Q4Mmgrm_VMHN-$!FA&~IDRD`meSI^ z_(H`L+}(d*Rcz~jwS}EbMaJ5uFKxuE|2(A-szDJ<%JdxoHl1 zXsPzz>{_!ZAcD9s;zP&lBE;-X+5e5->bb&n)H=--<6 zzhTS+yKik?=i%}58PL@P!TXJe-6l>6Bpnc=%B=e0-hU9Wvc-MkmPr10mf^0f|Dyr8 zAWIf3)@tvK6H1CC*M=lRGvQO&_ztpICB1E}X{*XI%kZlliYY8>(%E}oBat%c$$oih zgjrwHK>H&T&lS-0lvX0F+AlxukK|0-*9YxL#=Nb-8ze@e7s@~JD6tA*bF6$Ff|;06 zvG}vVxF1bi0f9|{bi0u;DF=b)+=8^}peR2rGbfdS zL1SX=L|c!;4l+mM?U!ij@|6h$o(Pz$)6vDD7j#r9LL;{|Ox8#5Z<89YXIJ;)2T=td zk~hA3@Mz8I=G6)ubqzbX{krsm9{r!>)Gfi^_xt0!dyn7AKFAH3k@Vv@qpRuH5F53W z;?+(ML~SM9IO@)How*?N@+eQt@$0rWHG8{1>3lD)@0&2kT;hRX$MYG>W*?D0a(VW$ zW0mtQ&Uh{IsqwQ}cA2$Pey5)0pJO{SHcwA)`|6W5&%#GX#5&?tLf9qIN1Bg1)~uNF z{;ZWw@x4cDLbBGB`u{tW5EtuNJolNvqX;J^PmT!z&XS@tIxel6l%(ML+fe-fg17#Y zyp_Twdt|s*I5lScKKTDZ`F6L&qC=is#~Rj&pZ+Dm$h6yGb6({AYg=RY>uqXp8K+wxI51i5)gs-W8CBe@5B~P-(`5g0pJ{KlU;f(K zId%+^ud;r!Sy-^Rbx+y*?$f(>=kEQpz5MDu`{wmU2j{zeIsuFq&H|6fVg?3oVGw3y zm^DWND9B#o>Fdh=l2evXT*TeFzyc^FS>hT|;+&tGo0?a`;9QiNSdyBeP@Y+mq2TW6 z8xY>eC(gjYwA<6gF{I+w+v^*7nH2>X9FqfCL>Rxs3zjq%vmfc0%{l)Ce`e{Di@*Lc zDAx8eurLTPC`^jU;{19zd)?#8EtM{h|K&0?0L=kH4h97XlPZh`1_?&O23_}D{56*+ zff?*F2T;-lIq?DK&;QOVkA}!?duBCn_y36R`OFOd3~*`cFjx+75NA*WH!%Fn*fubt mhS7(X(QpJs7n$MsVUN7SdnM;TOL}S@m diff --git a/examples/starting-3d-point-and-click-adventure/assets/StartingWater.png b/examples/starting-3d-point-and-click-adventure/assets/StartingWater.png index 2adc0635a45c59fbeb1754ccc31caf7fdde3f640..bbcab14e698cee33a23864d423265e91512a8679 100644 GIT binary patch delta 2791 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 literal 2748 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 S*$6+C0EDfhP4yXn^1lIEkUuy8 diff --git a/examples/starting-3d-point-and-click-adventure/assets/starter_pop.wav b/examples/starting-3d-point-and-click-adventure/assets/starter_pop.wav new file mode 100644 index 0000000000000000000000000000000000000000..5bc5472627e92d7209d23a33503f9721482a134a GIT binary patch literal 43564 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 literal 0 HcmV?d00001 diff --git a/examples/starting-3d-point-and-click-adventure/starting-3d-point-and-click-adventure.json b/examples/starting-3d-point-and-click-adventure/starting-3d-point-and-click-adventure.json index bbb293498..d82e09114 100644 --- a/examples/starting-3d-point-and-click-adventure/starting-3d-point-and-click-adventure.json +++ b/examples/starting-3d-point-and-click-adventure/starting-3d-point-and-click-adventure.json @@ -1,7 +1,7 @@ { "firstLayout": "", "gdVersion": { - "build": 272, + "build": 274, "major": 5, "minor": 6, "revision": 0 @@ -216,6 +216,34 @@ "metadata": "", "name": "assets\\grass_multiple_blades.glb", "userAdded": false + }, + { + "file": "assets/DirtParticle.png", + "kind": "image", + "metadata": "", + "name": "assets/DirtParticle.png", + "smoothed": true, + "userAdded": false + }, + { + "file": "assets/starter_pop.wav", + "kind": "audio", + "metadata": "", + "name": "assets/starter_pop.wav", + "preloadAsMusic": false, + "preloadAsSound": false, + "preloadInCache": false, + "userAdded": false + }, + { + "file": "assets/starting_bookopen.wav", + "kind": "audio", + "metadata": "", + "name": "assets/starting_bookopen.wav", + "preloadAsMusic": false, + "preloadAsSound": false, + "preloadInCache": false, + "userAdded": false } ] }, @@ -671,6 +699,7 @@ "assetStoreId": "", "height": 64, "name": "PathfindingBoundary", + "persistentUuid": "d5f804fa-fffe-440a-9286-7dbf33d6559f", "texture": "assets\\StartingCollisionArea.png", "type": "TiledSpriteObject::TiledSprite", "width": 64, @@ -688,6 +717,7 @@ { "assetStoreId": "9d5de415b6e469c681701ea42d3b1c486fee31d6ef591a2d3e226f639ab486ff", "name": "TwoChoicesDialogBox", + "persistentUuid": "89c797d1-d881-43ce-8f25-aba46f13610b", "type": "TwoChoicesDialogBoxes::TwoChoicesDialogBox", "variant": "DialogBox", "variables": [], @@ -740,6 +770,7 @@ { "assetStoreId": "", "name": "Wall", + "persistentUuid": "5d3220a5-4258-45a2-9177-c88078fe1cca", "type": "Scene3D::Cube3DObject", "variables": [], "effects": [], @@ -778,6 +809,7 @@ { "assetStoreId": "", "name": "Water", + "persistentUuid": "a602bcea-d476-49d3-b92a-eefd6f1ae794", "type": "Scene3D::Cube3DObject", "variables": [], "effects": [], @@ -897,18 +929,25 @@ { "assetStoreId": "", "name": "NPC", + "persistentUuid": "b1e4c841-d32e-4815-b481-bf19ceaf03d6", "type": "Scene3D::Model3DObject", "variables": [ { "name": "Selected", + "persistentUuid": "5865b5a1-2e82-402f-af68-40dc2ef53b38", "type": "boolean", "value": true } ], "effects": [], - "behaviors": [], + "behaviors": [ + { + "name": "Tween", + "type": "Tween::TweenBehavior" + } + ], "content": { - "centerLocation": "ModelOrigin", + "centerLocation": "ObjectCenter", "crossfadeDuration": 0.10000000149011612, "depth": 100, "height": 100, @@ -949,6 +988,87 @@ "width": 100, "animations": [] } + }, + { + "assetStoreId": "", + "name": "Debris", + "persistentUuid": "f3acad9c-8b1d-4abc-9e11-5c10a2202f11", + "type": "ParticleEmitter3D::ParticleEmitter3D", + "variant": "", + "variables": [], + "effects": [], + "behaviors": [], + "content": { + "EndColor": "255;255;255", + "StartColor": "109;109;109", + "SpayConeAngle": 360, + "EndScale": 1, + "Gravity": 600, + "StartSpeedMax": 500, + "StartSizeMin": 32, + "Duration": 0.01, + "Flow": 2000, + "LifespanMin": 0.075, + "LifespanMax": 0.25, + "StartSizeMax": 32, + "StartSpeedMin": 300 + }, + "childrenContent": { + "Particle": { + "adaptCollisionMaskAutomatically": true, + "updateIfNotVisible": false, + "animations": [ + { + "name": "Image", + "useMultipleDirections": false, + "directions": [ + { + "looping": false, + "timeBetweenFrames": 0.08, + "sprites": [ + { + "hasCustomCollisionMask": true, + "image": "assets/DirtParticle.png", + "points": [], + "originPoint": { + "name": "origine", + "x": 0, + "y": 0 + }, + "centerPoint": { + "automatic": true, + "name": "centre", + "x": 0, + "y": 0 + }, + "customCollisionMask": [ + [ + { + "x": 0, + "y": 0 + }, + { + "x": 32, + "y": 0 + }, + { + "x": 32, + "y": 32 + }, + { + "x": 0, + "y": 32 + } + ] + ] + } + ] + } + ] + } + ] + } + } } ], "objectsFolderStructure": { @@ -960,6 +1080,9 @@ { "objectName": "NPC" }, + { + "objectName": "Debris" + }, { "objectName": "Wall" }, @@ -1222,6 +1345,18 @@ "", "\"Dialog layer\"" ] + }, + { + "type": { + "value": "PlaySound" + }, + "parameters": [ + "", + "assets/starting_bookopen.wav", + "", + "80", + "1.1" + ] } ] } @@ -1285,6 +1420,41 @@ } ], "actions": [ + { + "type": { + "value": "Create" + }, + "parameters": [ + "", + "Debris", + "NPC.CenterX()", + "NPC.CenterY()", + "" + ] + }, + { + "type": { + "value": "Scene3D::Base3DBehavior::SetCenterZ" + }, + "parameters": [ + "Debris", + "Object3D", + "=", + "NPC.Object3D::CenterZ()" + ] + }, + { + "type": { + "value": "PlaySound" + }, + "parameters": [ + "", + "assets/starter_pop.wav", + "", + "80", + "RandomFloatInRange(0.7,0.9)" + ] + }, { "type": { "value": "Delete" @@ -1356,6 +1526,18 @@ "False", "" ] + }, + { + "type": { + "value": "PlaySound" + }, + "parameters": [ + "", + "assets/starting_bookopen.wav", + "", + "80", + "0.8" + ] } ] } @@ -1489,6 +1671,10 @@ { "name": "Text", "type": "TextContainerCapability::TextContainerBehavior" + }, + { + "name": "Tween", + "type": "Tween::TweenBehavior" } ] } @@ -18200,11 +18386,13 @@ { "assetStoreId": "90d94b6e3a23c34948e59eaaef4679740f8ab588696d59b21e0c0c2ef106b94d", "name": "NoButton", + "persistentUuid": "fa460b84-5fb4-4ffd-b1f5-f392f00be3cd", "type": "PanelSpriteButton::PanelSpriteButton", "variant": "OptionButton", "variables": [ { "name": "ID", + "persistentUuid": "29dacfe9-b409-4e8d-93ca-1724e9f5520c", "type": "number", "value": 0 } @@ -18248,11 +18436,13 @@ { "assetStoreId": "90d94b6e3a23c34948e59eaaef4679740f8ab588696d59b21e0c0c2ef106b94d", "name": "YesButton", + "persistentUuid": "a51cfd2d-0621-453e-9294-f911e0c5355e", "type": "PanelSpriteButton::PanelSpriteButton", "variant": "OptionButton", "variables": [ { "name": "ID", + "persistentUuid": "095ce0fb-66aa-4833-8a9c-c35450bb00be", "type": "number", "value": 0 } @@ -18295,14 +18485,15 @@ }, { "assetStoreId": "", - "bottomMargin": 16, + "bottomMargin": 18, "height": 48, - "leftMargin": 16, + "leftMargin": 18, "name": "TextBorder", - "rightMargin": 16, + "persistentUuid": "30472caa-af85-4597-91a0-07e6c57022df", + "rightMargin": 18, "texture": "Border.png", "tiled": true, - "topMargin": 16, + "topMargin": 18, "type": "PanelSpriteObject::PanelSprite", "width": 48, "variables": [], @@ -18450,13 +18641,13 @@ "angle": 0, "customSize": true, "depth": 32, - "height": 36, + "height": 46, "layer": "", "name": "YesButton", "persistentUuid": "a0ca78d3-0a23-4bbe-89de-fb2d0a53996c", "width": 105, "x": 350, - "y": 160, + "y": 150, "zOrder": 510, "numberProperties": [], "stringProperties": [], @@ -18466,13 +18657,13 @@ "angle": 0, "customSize": true, "depth": 32, - "height": 36, + "height": 46, "layer": "", "name": "NoButton", "persistentUuid": "6781aafa-3ae9-4820-bb97-682a524a0cf6", "width": 103, "x": 471, - "y": 160, + "y": 150, "zOrder": 510, "numberProperties": [], "stringProperties": [], @@ -18486,7 +18677,23 @@ ] } ], - "editionSettings": {} + "editionSettings": { + "grid": false, + "gridType": "rectangular", + "gridWidth": 32, + "gridHeight": 32, + "gridDepth": 32, + "gridOffsetX": 0, + "gridOffsetY": 0, + "gridOffsetZ": 0, + "gridColor": 10401023, + "gridAlpha": 0.8, + "snap": false, + "zoomFactor": 1.5724408145053925, + "windowMask": false, + "selectedLayer": "", + "gameEditorMode": "instances-editor" + } }, { "areaMaxX": 600, @@ -22847,20 +23054,22 @@ }, { "assetStoreId": "", - "bottomMargin": 11, + "bottomMargin": 18, "height": 106, - "leftMargin": 11, + "leftMargin": 18, "name": "Idle", - "rightMargin": 11, + "persistentUuid": "dee30395-0caf-424e-a628-93e660154a9e", + "rightMargin": 18, "texture": "Border.png", "tiled": false, - "topMargin": 11, + "topMargin": 18, "type": "PanelSpriteObject::PanelSprite", "width": 256, "variables": [ { "folded": true, "name": "State", + "persistentUuid": "e3d9d7f2-b706-416e-8953-eca56d76cbc2", "type": "string", "value": "Idle" } @@ -22886,14 +23095,15 @@ }, { "assetStoreId": "", - "bottomMargin": 8, + "bottomMargin": 18, "height": 106, - "leftMargin": 8, + "leftMargin": 18, "name": "Hovered", - "rightMargin": 8, + "persistentUuid": "ea5b26d2-6402-498e-b816-b94a6430255e", + "rightMargin": 18, "texture": "Border2.png", "tiled": false, - "topMargin": 8, + "topMargin": 18, "type": "PanelSpriteObject::PanelSprite", "width": 256, "variables": [], @@ -22917,14 +23127,15 @@ }, { "assetStoreId": "", - "bottomMargin": 8, + "bottomMargin": 18, "height": 106, - "leftMargin": 8, + "leftMargin": 18, "name": "Pressed", - "rightMargin": 8, + "persistentUuid": "87d0189e-e31b-4973-9a0e-1990361f8433", + "rightMargin": 18, "texture": "Border2.png", "tiled": false, - "topMargin": 8, + "topMargin": 18, "type": "PanelSpriteObject::PanelSprite", "width": 256, "variables": [], @@ -23095,7 +23306,23 @@ "initialVariables": [] } ], - "editionSettings": {} + "editionSettings": { + "grid": false, + "gridType": "rectangular", + "gridWidth": 32, + "gridHeight": 32, + "gridDepth": 32, + "gridOffsetX": 0, + "gridOffsetY": 0, + "gridOffsetZ": 0, + "gridColor": 10401023, + "gridAlpha": 0.8, + "snap": false, + "zoomFactor": 2.734375, + "windowMask": false, + "selectedLayer": "", + "gameEditorMode": "instances-editor" + } }, { "areaMaxX": 256, @@ -30102,6 +30329,11147 @@ }, "eventsBasedBehaviors": [], "eventsBasedObjects": [] + }, + { + "author": "", + "category": "Visual effect", + "dimension": "3D", + "extensionNamespace": "", + "fullName": "3D particle emitter", + "gdevelopVersion": ">=5.5.222", + "helpPath": "", + "iconUrl": "data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iVVRGLTgiPz48IURPQ1RZUEUgc3ZnIFBVQkxJQyAiLS8vVzNDLy9EVEQgU1ZHIDEuMS8vRU4iICJodHRwOi8vd3d3LnczLm9yZy9HcmFwaGljcy9TVkcvMS4xL0RURC9zdmcxMS5kdGQiPjxzdmcgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIiB4bWxuczp4bGluaz0iaHR0cDovL3d3dy53My5vcmcvMTk5OS94bGluayIgdmVyc2lvbj0iMS4xIiBpZD0ibWRpLWZpcmUiIHdpZHRoPSIyNCIgaGVpZ2h0PSIyNCIgdmlld0JveD0iMCAwIDI0IDI0Ij48cGF0aCBkPSJNMTcuNjYgMTEuMkMxNy40MyAxMC45IDE3LjE1IDEwLjY0IDE2Ljg5IDEwLjM4QzE2LjIyIDkuNzggMTUuNDYgOS4zNSAxNC44MiA4LjcyQzEzLjMzIDcuMjYgMTMgNC44NSAxMy45NSAzQzEzIDMuMjMgMTIuMTcgMy43NSAxMS40NiA0LjMyQzguODcgNi40IDcuODUgMTAuMDcgOS4wNyAxMy4yMkM5LjExIDEzLjMyIDkuMTUgMTMuNDIgOS4xNSAxMy41NUM5LjE1IDEzLjc3IDkgMTMuOTcgOC44IDE0LjA1QzguNTcgMTQuMTUgOC4zMyAxNC4wOSA4LjE0IDEzLjkzQzguMDggMTMuODggOC4wNCAxMy44MyA4IDEzLjc2QzYuODcgMTIuMzMgNi42OSAxMC4yOCA3LjQ1IDguNjRDNS43OCAxMCA0Ljg3IDEyLjMgNSAxNC40N0M1LjA2IDE0Ljk3IDUuMTIgMTUuNDcgNS4yOSAxNS45N0M1LjQzIDE2LjU3IDUuNyAxNy4xNyA2IDE3LjdDNy4wOCAxOS40MyA4Ljk1IDIwLjY3IDEwLjk2IDIwLjkyQzEzLjEgMjEuMTkgMTUuMzkgMjAuOCAxNy4wMyAxOS4zMkMxOC44NiAxNy42NiAxOS41IDE1IDE4LjU2IDEyLjcyTDE4LjQzIDEyLjQ2QzE4LjIyIDEyIDE3LjY2IDExLjIgMTcuNjYgMTEuMk0xNC41IDE3LjVDMTQuMjIgMTcuNzQgMTMuNzYgMTggMTMuNCAxOC4xQzEyLjI4IDE4LjUgMTEuMTYgMTcuOTQgMTAuNSAxNy4yOEMxMS42OSAxNyAxMi40IDE2LjEyIDEyLjYxIDE1LjIzQzEyLjc4IDE0LjQzIDEyLjQ2IDEzLjc3IDEyLjMzIDEzQzEyLjIxIDEyLjI2IDEyLjIzIDExLjYzIDEyLjUgMTAuOTRDMTIuNjkgMTEuMzIgMTIuODkgMTEuNyAxMy4xMyAxMkMxMy45IDEzIDE1LjExIDEzLjQ0IDE1LjM3IDE0LjhDMTUuNDEgMTQuOTQgMTUuNDMgMTUuMDggMTUuNDMgMTUuMjNDMTUuNDYgMTYuMDUgMTUuMSAxNi45NSAxNC41IDE3LjVIMTQuNVoiIC8+PC9zdmc+", + "name": "ParticleEmitter3D", + "previewIconUrl": "https://asset-resources.gdevelop.io/public-resources/Icons/f2e5a34bf465f781866677762d385d6c8e9e8d203383f2df9a3b7e0fad6a2cb5_fire.svg", + "shortDescription": "Display a large number of particles in 3D to create visual effects in a 3D game.", + "version": "3.1.0", + "description": [ + "3D particle emitters let you create and display many small particles to simulate visual effects in your game — like fire, explosions, smoke, or dust.", + "", + "The parameters of the object can be configured in multiple different ways to create a specific desired effect. GDevelop will give a set of a pre-configured 3D particle emitters that you should try first.", + "", + "Use these 3D particle emitters in 3D games. For 2D games or particles that appear on a game's 2D user interface, use the 2D particle emitter object instead." + ], + "origin": { + "identifier": "ParticleEmitter3D", + "name": "gdevelop-extension-store" + }, + "tags": [ + "3d", + "particle", + "explosion", + "fire", + "smoke", + "splash", + "light" + ], + "authorIds": [ + "IWykYNRvhCZBN3vEgKEbBPOR3Oc2" + ], + "changelog": [ + { + "version": "3.0.0", + "breaking": "- the object rotates the other way around X axis." + }, + { + "version": "2.0.0", + "breaking": "- Object properties for position and rotation have been removed. They must be set with the instance editor or the action." + }, + { + "version": "1.0.0", + "breaking": "- Particles were 3 times too small." + } + ], + "dependencies": [], + "globalVariables": [], + "sceneVariables": [], + "eventsFunctions": [ + { + "description": "Define helper classes JavaScript code.", + "fullName": "Define helper classes", + "functionType": "Action", + "name": "DefineHelperClasses", + "private": true, + "sentence": "Define helper classes JavaScript code", + "events": [ + { + "type": "BuiltinCommonInstructions::JsCode", + "inlineCode": [ + "if (gdjs.__particleEmmiter3DExtension) {", + " return;", + "}", + "", + "const game = runtimeScene.getGame();", + "", + "class ParticleEmitter3DRenderer extends gdjs.CustomRuntimeObject3DRenderer {", + " constructor(", + " object,", + " instanceContainer,", + " parent", + " ) {", + " super(object, instanceContainer, parent);", + " }", + "", + " _updateThreeGroup() {", + " const threeObject3D = this.get3DRendererObject();", + "", + " threeObject3D.rotation.set(", + " gdjs.toRad(this._object.getRotationX()),", + " gdjs.toRad(this._object.getRotationY()),", + " gdjs.toRad(this._object.angle)", + " );", + "", + " threeObject3D.position.set(", + " this._object.getX(),", + " this._object.getY(),", + " this._object.getZ()", + " );", + "", + " // Force the scale to 1 because the particle emitter doesn't really has a size.", + " threeObject3D.scale.set(", + " this._object.isFlippedX() ? -1 : 1,", + " this._object.isFlippedY() ? -1 : 1,", + " this._object.isFlippedZ() ? -1 : 1", + " );", + "", + " threeObject3D.visible = !this._object.hidden;", + "", + " this._isContainerDirty = true;", + " }", + "}", + "", + "const coneLength = 64;", + "", + "class ParticleEmitterHelper extends THREE.Object3D {", + " /** @type {gdjs.CustomRuntimeObject3D} */", + " object;", + " /** @type {THREE.LineSegments} */", + " cone;", + " /** @type {THREE.LineSegments} */", + " centerLine;", + " /** @type {THREE.Mesh} */", + " originBox;", + "", + "\t/**", + " * @param gdjs.CustomRuntimeObject3D object", + "\t */", + " constructor(object) {", + " super();", + " this.object = object;", + " this.type = 'ParticleEmitterHelper';", + "", + " const coneGeometry = new THREE.BufferGeometry();", + " const conePositions = [", + " 0, 0, 0, 0, 0, 1,", + " 0, 0, 0, 1, 0, 1,", + " 0, 0, 0, - 1, 0, 1,", + " 0, 0, 0, 0, 1, 1,", + " 0, 0, 0, 0, - 1, 1", + " ];", + " for (let i = 0, j = 1, l = 32; i < l; i++ , j++) {", + " const p1 = (i / l) * Math.PI * 2;", + " const p2 = (j / l) * Math.PI * 2;", + " conePositions.push(", + " Math.cos(p1), Math.sin(p1), 1,", + " Math.cos(p2), Math.sin(p2), 1", + " );", + " }", + " coneGeometry.setAttribute('position', new THREE.Float32BufferAttribute(conePositions, 3));", + " this.cone = new THREE.LineSegments(", + " coneGeometry,", + " new THREE.LineBasicMaterial({ fog: false, toneMapped: false })", + " );", + " this.add(this.cone);", + " const centerLineGeometry = new THREE.BufferGeometry();", + " centerLineGeometry.setAttribute('position', new THREE.Float32BufferAttribute([", + " 0, 0, 0, 0, 0, coneLength", + " ], 3));", + " this.centerLine = new THREE.LineSegments(", + " centerLineGeometry,", + " new THREE.LineBasicMaterial({ fog: false, toneMapped: false })", + " );", + " this.add(this.centerLine);", + "", + " this.originBox = new THREE.Mesh(", + " new THREE.BoxGeometry(16, 16, 16),", + " new THREE.MeshBasicMaterial({ fog: false, toneMapped: false })", + " );", + " this.add(this.originBox);", + " this.update();", + "", + " this.originBox.gdjsRuntimeObject = object;", + " }", + "", + " dispose() {", + " this.cone.geometry.dispose();", + " this.cone.material.dispose();", + " this.centerLine.geometry.dispose();", + " this.centerLine.material.dispose();", + " this.originBox.geometry.dispose();", + " this.originBox.material.dispose();", + " }", + "", + " update() {", + " const coneWidth = coneLength * Math.sin(gdjs.toRad(this.object._getSpayConeAngle()));", + " const coneHeight = coneLength * Math.cos(gdjs.toRad(this.object._getSpayConeAngle()));", + " this.cone.scale.set(coneWidth, coneWidth, coneHeight);", + " const startColor = gdjs.rgbOrHexStringToNumber(this.object._getStartColor())", + " const endColor = gdjs.rgbOrHexStringToNumber(this.object._getEndColor());", + " this.cone.material.color.set(endColor);", + " this.centerLine.material.color.set(startColor);", + " this.originBox.material.color.set(startColor);", + " }", + "}", + "", + "/**", + " * @param {string} colorString", + " * @param {THREE.Vector4} threeColor", + " */", + "const setThreeColor = (colorString, threeColor = new THREE.Vector4()) => {", + " const integerColor = gdjs.rgbOrHexToRGBColor(colorString);", + " threeColor.x = integerColor[0] / 255;", + " threeColor.y = integerColor[1] / 255;", + " threeColor.z = integerColor[2] / 255;", + "};", + "", + "/**", + " * @param {string} integerOpacity", + " * @param {THREE.Vector4} threeColor", + " */", + "const setThreeOpacity = (integerOpacity, threeColor = new THREE.Vector4()) => {", + " threeColor.w = integerOpacity / 255;", + "};", + "", + "class ParticleEmitterAdapter {", + " /**", + " * @param particleSystem {ParticleSystem}", + " * @param colorOverLife {ColorOverLife}", + " * @param sizeOverLife {SizeOverLife}", + " * @param applyForce {ApplyForce}", + " * @param applyForce {ApplyForce}", + " * @param widthOverLength {WidthOverLength}", + " */", + " constructor(particleSystem, colorOverLife, sizeOverLife, applyForce, widthOverLength) {", + " this.particleSystem = particleSystem;", + " this.colorOverLife = colorOverLife;", + " this.sizeOverLife = sizeOverLife;", + " this.applyForce = applyForce;", + " this.widthOverLength = widthOverLength;", + " }", + "", + " /**", + " * @param renderMode {'Billboard' | 'Trail'}", + " */", + " setRenderMode(renderMode) {", + " switch (renderMode) {", + " case \"BillBoard\":", + " this.particleSystem.renderMode = RenderMode.BillBoard;", + " break;", + " case \"Trail\":", + " this.particleSystem.renderMode = RenderMode.Trail;", + " this.particleSystem.rendererEmitterSettings.startLength = new IntervalValue(30, 30);", + " break;", + " }", + " }", + "", + " /**", + " * @param startLengthMin {number}", + " */", + " setTrailStartLengthMin(startLengthMin) {", + " const startLength = this.particleSystem.rendererEmitterSettings.startLength;", + " if (!startLength) {", + " return;", + " }", + " startLength.a = startLengthMin;", + " }", + "", + " /**", + " * @param startLengthMax {number}", + " */", + " setTrailStartLengthMax(startLengthMax) {", + " const startLength = this.particleSystem.rendererEmitterSettings.startLength;", + " if (!startLength) {", + " return;", + " }", + " startLength.b = startLengthMax;", + " }", + "", + " /**", + " * @param followLocalOrigin {boolean}", + " */", + " setTrailFollowingLocalOrigin(followLocalOrigin) {", + " this.particleSystem.rendererEmitterSettings.followLocalOrigin = followLocalOrigin;", + " }", + "", + " /**", + " * @param trailEndWidthRatio {number}", + " */", + " setTrailEndWidthRatio(trailEndWidthRatio) {", + " // Check this comment in the lib code:", + " // We added a multiplication with size because it's more convenient for the extension.", + "", + " /** @type Bezier */", + " const bezier = this.widthOverLength.width.functions[0][0];", + " bezier.p[0] = 1;", + " bezier.p[1] = gdjs.evtTools.common.lerp(1, trailEndWidthRatio, 1 / 3);", + " bezier.p[2] = gdjs.evtTools.common.lerp(1, trailEndWidthRatio, 2 / 3);", + " bezier.p[3] = trailEndWidthRatio;", + " }", + "", + "", + " /**", + " * @param startColor {string}", + " */", + " setStartColor(startColor) {", + " setThreeColor(startColor, this.colorOverLife.color.color.keys[0][0]);", + " }", + "", + " /**", + " * @param endColor {string}", + " */", + " setEndColor(endColor) {", + " setThreeColor(endColor, this.colorOverLife.color.color.keys[1][0]);", + " }", + "", + " /**", + " * @param startOpacity {number}", + " */", + " setStartOpacity(startOpacity) {", + " this.colorOverLife.color.alpha.keys[0][0] = startOpacity / 255;", + " }", + "", + " /**", + " * @param endOpacity {number}", + " */", + " setEndOpacity(endOpacity) {", + " this.colorOverLife.color.alpha.keys[1][0] = endOpacity / 255;", + " }", + "", + " /**", + " * @param flow {number}", + " */", + " setFlow(flow) {", + " this.particleSystem.emissionOverTime.value = flow;", + " }", + "", + " /**", + " * @param startMinSize {number}", + " */", + " setStartMinSize(startMinSize) {", + " this.particleSystem.startSize.a = startMinSize;", + " }", + "", + " /**", + " * @param startMaxSize {number}", + " */", + " setStartMaxSize(startMaxSize) {", + " this.particleSystem.startSize.b = startMaxSize;", + " }", + "", + " /**", + " * @param endScale {number}", + " */", + " setEndScale(endScale) {", + " /** @type Bezier */", + " const bezier = this.sizeOverLife.size.functions[0][0];", + " bezier.p[0] = 1;", + " bezier.p[1] = gdjs.evtTools.common.lerp(1, endScale, 1 / 3);", + " bezier.p[2] = gdjs.evtTools.common.lerp(1, endScale, 2 / 3);", + " bezier.p[3] = endScale;", + " }", + "", + " /**", + " * @param startSpeedMin {number}", + " */", + " setStartSpeedMin(startSpeedMin) {", + " this.particleSystem.startSpeed.a = startSpeedMin;", + " }", + "", + " /**", + " * @param startSpeedMax {number}", + " */", + " setStartSpeedMax(startSpeedMax) {", + " this.particleSystem.startSpeed.b = startSpeedMax;", + " }", + "", + " /**", + " * @param lifespanMin {number}", + " */", + " setLifespanMin(lifespanMin) {", + " this.particleSystem.startLife.a = lifespanMin;", + " }", + "", + " /**", + " * @param lifespanMax {number}", + " */", + " setLifespanMax(lifespanMax) {", + " this.particleSystem.startLife.b = lifespanMax;", + " }", + "", + " /**", + " * @param duration {number}", + " */", + " setDuration(duration) {", + " this.particleSystem.duration = duration || Number.POSITIVE_INFINITY;", + " }", + "", + " /**", + " * @param areParticlesRelative {boolean}", + " */", + " setParticlesRelative(areParticlesRelative) {", + " this.particleSystem.worldSpace = !areParticlesRelative;", + " }", + "", + " /**", + " * @param sprayConeAngle {number}", + " */", + " setSprayConeAngle(sprayConeAngle) {", + " if (0 < sprayConeAngle && sprayConeAngle <= 180) {", + " if (!this.particleSystem.emitterShape.angle) {", + " this.particleSystem.emitterShape = new ConeEmitter({ radius: 0.1, angle: Math.PI / 8 });", + " }", + " this.particleSystem.emitterShape.angle = sprayConeAngle * Math.PI / 180;", + " }", + " else {", + " if (this.particleSystem.emitterShape.angle) {", + " this.particleSystem.emitterShape = new PointEmitter();", + " }", + " }", + " }", + "", + " /**", + " * @param blending {string}", + " */", + " setBlending(blendingString) {", + " const blending =", + " blendingString === \"Additive\" ? THREE.AdditiveBlending :", + " blendingString === \"Normal\" ? THREE.NormalBlending :", + " blendingString === \"Subtractive\" ? THREE.SubtractiveBlending :", + " blendingString === \"Multiply\" ? THREE.MultiplyBlending :", + " blendingString === \"None\" ? THREE.NoBlending :", + " THREE.AdditiveBlending;", + " // TODO This doesn't work.", + " this.particleSystem.blending = blending;", + " }", + "", + " /**", + " * @param gravity {number}", + " */", + " setGravity(gravity) {", + " this.applyForce.magnitude.value = gravity;", + " }", + "", + " /**", + " * @param gravityTop {string}", + " */", + " setGravityTop(gravityTop) {", + " // TODO Make gravity absolute even when \"relative\" is enabled. ", + " switch (gravityTop) {", + " case \"Z+\":", + " this.applyForce.direction.set(0, 0, -1);", + " break;", + "", + " case \"Y-\":", + " this.applyForce.direction.set(0, 1, 0);", + " break;", + " }", + " }", + "", + " setImage(resourceName) {", + " const texture = game.getImageManager().getThreeTexture(resourceName);", + " this.particleSystem.texture = texture;", + " }", + "}", + "", + "", + "", + "/**", + " * three.quarks v0.11.2 build Mon Jan 22 2024", + " * https://github.com/Alchemist0823/three.quarks#readme", + " * Copyright 2024 Alchemist0823 , MIT", + " */", + "", + "// The migration to addUpdateRange was backported without updating the library", + "// because GDevelop still uses Three.js 0.160.0.", + "", + "class ParticleEmitter extends THREE.Object3D {", + " constructor(system) {", + " super();", + " this.type = 'ParticleEmitter';", + " this.system = system;", + " }", + " clone() {", + " const system = this.system.clone();", + " system.emitter.copy(this, true);", + " return system.emitter;", + " }", + " dispose() { }", + " extractFromCache(cache) {", + " const values = [];", + " for (const key in cache) {", + " const data = cache[key];", + " delete data.metadata;", + " values.push(data);", + " }", + " return values;", + " }", + " toJSON(meta, options = {}) {", + " const isRootObject = meta === undefined || typeof meta === 'string';", + " const output = {};", + " if (isRootObject) {", + " meta = {", + " geometries: {},", + " materials: {},", + " textures: {},", + " images: {},", + " shapes: {},", + " skeletons: {},", + " animations: {},", + " nodes: {},", + " };", + " output.metadata = {", + " version: 4.5,", + " type: 'Object',", + " generator: 'Object3D.toJSON',", + " };", + " }", + " const object = {};", + " object.uuid = this.uuid;", + " object.type = this.type;", + " if (this.name !== '')", + " object.name = this.name;", + " if (this.castShadow === true)", + " object.castShadow = true;", + " if (this.receiveShadow === true)", + " object.receiveShadow = true;", + " if (this.visible === false)", + " object.visible = false;", + " if (this.frustumCulled === false)", + " object.frustumCulled = false;", + " if (this.renderOrder !== 0)", + " object.renderOrder = this.renderOrder;", + " if (JSON.stringify(this.userData) !== '{}')", + " object.userData = this.userData;", + " object.layers = this.layers.mask;", + " object.matrix = this.matrix.toArray();", + " if (this.matrixAutoUpdate === false)", + " object.matrixAutoUpdate = false;", + " if (this.system !== null)", + " object.ps = this.system.toJSON(meta, options);", + " if (this.children.length > 0) {", + " object.children = [];", + " for (let i = 0; i < this.children.length; i++) {", + " if (this.children[i].type !== 'ParticleSystemPreview') {", + " object.children.push(this.children[i].toJSON(meta).object);", + " }", + " }", + " }", + " if (isRootObject) {", + " const geometries = this.extractFromCache(meta.geometries);", + " const materials = this.extractFromCache(meta.materials);", + " const textures = this.extractFromCache(meta.textures);", + " const images = this.extractFromCache(meta.images);", + " if (geometries.length > 0)", + " output.geometries = geometries;", + " if (materials.length > 0)", + " output.materials = materials;", + " if (textures.length > 0)", + " output.textures = textures;", + " if (images.length > 0)", + " output.images = images;", + " }", + " output.object = object;", + " return output;", + " }", + "}", + "", + "class LinkedListNode {", + " constructor(data) {", + " this.data = data;", + " this.next = null;", + " this.prev = null;", + " }", + " hasPrev() {", + " return this.prev !== null;", + " }", + " hasNext() {", + " return this.next !== null;", + " }", + "}", + "class LinkedList {", + " constructor() {", + " this.length = 0;", + " this.head = this.tail = null;", + " }", + " isEmpty() {", + " return this.head === null;", + " }", + " clear() {", + " this.length = 0;", + " this.head = this.tail = null;", + " }", + " front() {", + " if (this.head === null)", + " return null;", + " return this.head.data;", + " }", + " back() {", + " if (this.tail === null)", + " return null;", + " return this.tail.data;", + " }", + " dequeue() {", + " if (this.head) {", + " const value = this.head.data;", + " this.head = this.head.next;", + " if (!this.head) {", + " this.tail = null;", + " }", + " else {", + " this.head.prev = null;", + " }", + " this.length--;", + " return value;", + " }", + " return undefined;", + " }", + " pop() {", + " if (this.tail) {", + " const value = this.tail.data;", + " this.tail = this.tail.prev;", + " if (!this.tail) {", + " this.head = null;", + " }", + " else {", + " this.tail.next = null;", + " }", + " this.length--;", + " return value;", + " }", + " return undefined;", + " }", + " queue(data) {", + " const node = new LinkedListNode(data);", + " if (!this.tail) {", + " this.tail = node;", + " }", + " if (this.head) {", + " this.head.prev = node;", + " node.next = this.head;", + " }", + " this.head = node;", + " this.length++;", + " }", + " push(data) {", + " const node = new LinkedListNode(data);", + " if (!this.head) {", + " this.head = node;", + " }", + " if (this.tail) {", + " this.tail.next = node;", + " node.prev = this.tail;", + " }", + " this.tail = node;", + " this.length++;", + " }", + " insertBefore(node, data) {", + " const newNode = new LinkedListNode(data);", + " newNode.next = node;", + " newNode.prev = node.prev;", + " if (newNode.prev !== null) {", + " newNode.prev.next = newNode;", + " }", + " newNode.next.prev = newNode;", + " if (node == this.head) {", + " this.head = newNode;", + " }", + " this.length++;", + " }", + " remove(data) {", + " if (this.head === null || this.tail === null) {", + " return;", + " }", + " let tempNode = this.head;", + " if (data === this.head.data) {", + " this.head = this.head.next;", + " }", + " if (data === this.tail.data) {", + " this.tail = this.tail.prev;", + " }", + " while (tempNode.next !== null && tempNode.data !== data) {", + " tempNode = tempNode.next;", + " }", + " if (tempNode.data === data) {", + " if (tempNode.prev !== null)", + " tempNode.prev.next = tempNode.next;", + " if (tempNode.next !== null)", + " tempNode.next.prev = tempNode.prev;", + " this.length--;", + " }", + " }", + " *values() {", + " let current = this.head;", + " while (current !== null) {", + " yield current.data;", + " current = current.next;", + " }", + " }", + "}", + "", + "class NodeParticle {", + " constructor() {", + " this.position = new THREE.Vector3();", + " this.velocity = new THREE.Vector3();", + " this.age = 0;", + " this.life = 1;", + " this.size = 1;", + " this.rotation = 0;", + " this.color = new THREE.Vector4(1, 1, 1, 1);", + " this.uvTile = 0;", + " }", + " get died() {", + " return this.age >= this.life;", + " }", + " reset() {", + " this.position.set(0, 0, 0);", + " this.velocity.set(0, 0, 0);", + " this.age = 0;", + " this.life = 1;", + " this.size = 1;", + " this.rotation = 0;", + " this.color.set(1, 1, 1, 1);", + " this.uvTile = 0;", + " }", + "}", + "class SpriteParticle {", + " constructor() {", + " this.startSpeed = 0;", + " this.startColor = new THREE.Vector4();", + " this.startSize = 1;", + " this.position = new THREE.Vector3();", + " this.velocity = new THREE.Vector3();", + " this.age = 0;", + " this.life = 1;", + " this.size = 1;", + " this.speedModifier = 1;", + " this.rotation = 0;", + " this.color = new THREE.Vector4();", + " this.uvTile = 0;", + " }", + " get died() {", + " return this.age >= this.life;", + " }", + "}", + "class RecordState {", + " constructor(position, size, color) {", + " this.position = position;", + " this.size = size;", + " this.color = color;", + " }", + "}", + "class TrailParticle {", + " constructor() {", + " this.startSpeed = 0;", + " this.startColor = new THREE.Vector4();", + " this.startSize = 1;", + " this.position = new THREE.Vector3();", + " this.velocity = new THREE.Vector3();", + " this.age = 0;", + " this.life = 1;", + " this.size = 1;", + " this.length = 100;", + " this.speedModifier = 1;", + " this.color = new THREE.Vector4();", + " this.previous = new LinkedList();", + " this.uvTile = 0;", + " }", + " update() {", + " if (this.age <= this.life) {", + " this.previous.push(new RecordState(this.position.clone(), this.size, this.color.clone()));", + " }", + " else {", + " if (this.previous.length > 0) {", + " this.previous.dequeue();", + " }", + " }", + " while (this.previous.length > this.length) {", + " this.previous.dequeue();", + " }", + " }", + " get died() {", + " return this.age >= this.life;", + " }", + " reset() {", + " this.previous.clear();", + " }", + "}", + "", + "class Bezier {", + " constructor(p1, p2, p3, p4) {", + " this.p = [p1, p2, p3, p4];", + " }", + " genValue(t) {", + " const t2 = t * t;", + " const t3 = t * t * t;", + " const mt = 1 - t;", + " const mt2 = mt * mt;", + " const mt3 = mt2 * mt;", + " return this.p[0] * mt3 + this.p[1] * mt2 * t * 3 + this.p[2] * mt * t2 * 3 + this.p[3] * t3;", + " }", + " derivativeCoefficients(points) {", + " const dpoints = [];", + " for (let p = points, c = p.length - 1; c > 0; c--) {", + " const list = [];", + " for (let j = 0; j < c; j++) {", + " const dpt = c * (p[j + 1] - p[j]);", + " list.push(dpt);", + " }", + " dpoints.push(list);", + " p = list;", + " }", + " return dpoints;", + " }", + " getSlope(t) {", + " const p = this.derivativeCoefficients(this.p)[0];", + " const mt = 1 - t;", + " const a = mt * mt;", + " const b = mt * t * 2;", + " const c = t * t;", + " return a * p[0] + b * p[1] + c * p[2];", + " }", + " controlCurve(d0, d1) {", + " this.p[1] = d0 / 3 + this.p[0];", + " this.p[2] = this.p[3] - d1 / 3;", + " }", + " hull(t) {", + " let p = this.p;", + " let _p = [], pt, idx = 0, i = 0, l = 0;", + " const q = [];", + " q[idx++] = p[0];", + " q[idx++] = p[1];", + " q[idx++] = p[2];", + " q[idx++] = p[3];", + " while (p.length > 1) {", + " _p = [];", + " for (i = 0, l = p.length - 1; i < l; i++) {", + " pt = t * p[i] + (1 - t) * p[i + 1];", + " q[idx++] = pt;", + " _p.push(pt);", + " }", + " p = _p;", + " }", + " return q;", + " }", + " split(t) {", + " const q = this.hull(t);", + " const result = {", + " left: new Bezier(q[0], q[4], q[7], q[9]),", + " right: new Bezier(q[9], q[8], q[6], q[3]),", + " span: q", + " };", + " return result;", + " }", + " clone() {", + " return new Bezier(this.p[0], this.p[1], this.p[2], this.p[3]);", + " }", + " toJSON() {", + " return {", + " p0: this.p[0],", + " p1: this.p[1],", + " p2: this.p[2],", + " p3: this.p[3],", + " };", + " }", + " static fromJSON(json) {", + " return new Bezier(json.p0, json.p1, json.p2, json.p3);", + " }", + "}", + "", + "const ColorToJSON = (color) => {", + " return { r: color.x, g: color.y, b: color.z, a: color.w };", + "};", + "const JSONToColor = (json) => {", + " return new THREE.Vector4(json.r, json.g, json.b, json.a);", + "};", + "const JSONToValue = (json, type) => {", + " switch (type) {", + " case 'Vector3':", + " return new THREE.Vector3(json.x, json.y, json.z);", + " case 'Vector4':", + " return new THREE.Vector4(json.x, json.y, json.z, json.w);", + " case 'Color':", + " return new THREE.Vector3(json.r, json.g, json.b);", + " case 'Number':", + " return json;", + " default:", + " return json;", + " }", + "};", + "const ValueToJSON = (value, type) => {", + " switch (type) {", + " case 'Vector3':", + " return { x: value.x, y: value.y, z: value.z };", + " case 'Vector4':", + " return { x: value.x, y: value.y, z: value.z, w: value.w };", + " case 'Color':", + " return { r: value.x, g: value.y, b: value.z };", + " case 'Number':", + " return value;", + " default:", + " return value;", + " }", + "};", + "", + "class RandomColor {", + " constructor(a, b) {", + " this.a = a;", + " this.b = b;", + " this.type = \"value\";", + " }", + " genColor(color) {", + " const rand = Math.random();", + " return color.copy(this.a).lerp(this.b, rand);", + " }", + " toJSON() {", + " return {", + " type: \"RandomColor\",", + " a: ColorToJSON(this.a),", + " b: ColorToJSON(this.b),", + " };", + " }", + " static fromJSON(json) {", + " return new RandomColor(JSONToColor(json.a), JSONToColor(json.b));", + " }", + " clone() {", + " return new RandomColor(this.a.clone(), this.b.clone());", + " }", + "}", + "", + "class ColorRange {", + " constructor(a, b) {", + " this.a = a;", + " this.b = b;", + " this.type = 'value';", + " }", + " genColor(color, t) {", + " return color.copy(this.a).lerp(this.b, Math.random());", + " }", + " toJSON() {", + " return {", + " type: 'ColorRange',", + " a: ColorToJSON(this.a),", + " b: ColorToJSON(this.b),", + " };", + " }", + " static fromJSON(json) {", + " return new ColorRange(JSONToColor(json.a), JSONToColor(json.b));", + " }", + " clone() {", + " return new ColorRange(this.a.clone(), this.b.clone());", + " }", + "}", + "", + "class ContinuousLinearFunction {", + " constructor(keys, subType) {", + " this.subType = subType;", + " this.type = 'function';", + " this.keys = keys;", + " }", + " findKey(t) {", + " let mid = 0;", + " let left = 0, right = this.keys.length - 1;", + " while (left + 1 < right) {", + " mid = Math.floor((left + right) / 2);", + " if (t < this.getStartX(mid))", + " right = mid - 1;", + " else if (t > this.getEndX(mid))", + " left = mid + 1;", + " else", + " return mid;", + " }", + " for (let i = left; i <= right; i++) {", + " if (t >= this.getStartX(i) && t <= this.getEndX(i))", + " return i;", + " }", + " return -1;", + " }", + " getStartX(index) {", + " return this.keys[index][1];", + " }", + " getEndX(index) {", + " if (index + 1 < this.keys.length)", + " return this.keys[index + 1][1];", + " return 1;", + " }", + " genValue(value, t) {", + " const index = this.findKey(t);", + " if (this.subType === 'Number') {", + " if (index === -1) {", + " return this.keys[0][0];", + " }", + " else if (index + 1 >= this.keys.length) {", + " return this.keys[this.keys.length - 1][0];", + " }", + " return ((this.keys[index + 1][0] - this.keys[index][0]) *", + " ((t - this.getStartX(index)) / (this.getEndX(index) - this.getStartX(index))) +", + " this.keys[index][0]);", + " }", + " else {", + " if (index === -1) {", + " return value.copy(this.keys[0][0]);", + " }", + " if (index + 1 >= this.keys.length) {", + " return value.copy(this.keys[this.keys.length - 1][0]);", + " }", + " return value", + " .copy(this.keys[index][0])", + " .lerp(this.keys[index + 1][0], (t - this.getStartX(index)) / (this.getEndX(index) - this.getStartX(index)));", + " }", + " }", + " toJSON() {", + " this.keys[0][0].constructor.name;", + " return {", + " type: 'CLinearFunction',", + " subType: this.subType,", + " keys: this.keys.map(([color, pos]) => ({ value: ValueToJSON(color, this.subType), pos: pos })),", + " };", + " }", + " static fromJSON(json) {", + " return new ContinuousLinearFunction(json.keys.map((pair) => [JSONToValue(pair.value, json.subType), pair.pos]), json.subType);", + " }", + " clone() {", + " if (this.subType === 'Number') {", + " return new ContinuousLinearFunction(this.keys.map(([value, pos]) => [value, pos]), this.subType);", + " }", + " else {", + " return new ContinuousLinearFunction(this.keys.map(([value, pos]) => [value.clone(), pos]), this.subType);", + " }", + " }", + "}", + "", + "const tempVec3 = new THREE.Vector3();", + "class Gradient {", + " constructor(color = [", + " [new THREE.Vector3(0, 0, 0), 0],", + " [new THREE.Vector3(1, 1, 1), 0],", + " ], alpha = [", + " [1, 0],", + " [1, 1],", + " ]) {", + " this.type = 'function';", + " this.color = new ContinuousLinearFunction(color, 'Color');", + " this.alpha = new ContinuousLinearFunction(alpha, 'Number');", + " }", + " genColor(color, t) {", + " this.color.genValue(tempVec3, t);", + " return color.set(tempVec3.x, tempVec3.y, tempVec3.z, this.alpha.genValue(1, t));", + " }", + " toJSON() {", + " return {", + " type: 'Gradient',", + " color: this.color.toJSON(),", + " alpha: this.alpha.toJSON(),", + " };", + " }", + " static fromJSON(json) {", + " if (json.functions) {", + " const keys = json.functions.map((func) => [ColorRange.fromJSON(func.function).a, func.start]);", + " if (json.functions.length > 0) {", + " keys.push([ColorRange.fromJSON(json.functions[json.functions.length - 1].function).b, 1]);", + " }", + " return new Gradient(keys.map((key) => [new THREE.Vector3(key[0].x, key[0].y, key[0].z), key[1]]), keys.map((key) => [key[0].w, key[1]]));", + " }", + " else {", + " const gradient = new Gradient();", + " gradient.alpha = ContinuousLinearFunction.fromJSON(json.alpha);", + " gradient.color = ContinuousLinearFunction.fromJSON(json.color);", + " return gradient;", + " }", + " }", + " clone() {", + " const gradient = new Gradient();", + " gradient.alpha = this.alpha.clone();", + " gradient.color = this.color.clone();", + " return gradient;", + " }", + "}", + "", + "const tempColor = new THREE.Vector4();", + "class RandomColorBetweenGradient {", + " constructor(gradient1, gradient2) {", + " this.type = 'memorizedFunction';", + " this.gradient1 = gradient1;", + " this.gradient2 = gradient2;", + " }", + " startGen(memory) {", + " memory.rand = Math.random();", + " }", + " genColor(color, t, memory) {", + " this.gradient1.genColor(color, t);", + " this.gradient2.genColor(tempColor, t);", + " if (memory && memory.rand) {", + " color.lerp(tempColor, memory.rand);", + " }", + " else {", + " color.lerp(tempColor, Math.random());", + " }", + " return color;", + " }", + " toJSON() {", + " return {", + " type: 'RandomColorBetweenGradient',", + " gradient1: this.gradient1.toJSON(),", + " gradient2: this.gradient2.toJSON(),", + " };", + " }", + " static fromJSON(json) {", + " return new RandomColorBetweenGradient(Gradient.fromJSON(json.gradient1), Gradient.fromJSON(json.gradient2));", + " }", + " clone() {", + " return new RandomColorBetweenGradient(this.gradient1.clone(), this.gradient2.clone());", + " }", + "}", + "", + "class ConstantColor {", + " constructor(color) {", + " this.color = color;", + " this.type = 'value';", + " }", + " genColor(color) {", + " return color.copy(this.color);", + " }", + " toJSON() {", + " return {", + " type: 'ConstantColor',", + " color: ColorToJSON(this.color),", + " };", + " }", + " static fromJSON(json) {", + " return new ConstantColor(JSONToColor(json.color));", + " }", + " clone() {", + " return new ConstantColor(this.color.clone());", + " }", + "}", + "function ColorGeneratorFromJSON(json) {", + " switch (json.type) {", + " case 'ConstantColor':", + " return ConstantColor.fromJSON(json);", + " case 'ColorRange':", + " return ColorRange.fromJSON(json);", + " case 'RandomColor':", + " return RandomColor.fromJSON(json);", + " case 'Gradient':", + " return Gradient.fromJSON(json);", + " case 'RandomColorBetweenGradient':", + " return RandomColorBetweenGradient.fromJSON(json);", + " default:", + " return new ConstantColor(new THREE.Vector4(1, 1, 1, 1));", + " }", + "}", + "", + "class ConstantValue {", + " constructor(value) {", + " this.value = value;", + " this.type = 'value';", + " }", + " genValue() {", + " return this.value;", + " }", + " toJSON() {", + " return {", + " type: \"ConstantValue\",", + " value: this.value", + " };", + " }", + " static fromJSON(json) {", + " return new ConstantValue(json.value);", + " }", + " clone() {", + " return new ConstantValue(this.value);", + " }", + "}", + "", + "class IntervalValue {", + " constructor(a, b) {", + " this.a = a;", + " this.b = b;", + " this.type = 'value';", + " }", + " genValue() {", + " return THREE.MathUtils.lerp(this.a, this.b, Math.random());", + " }", + " toJSON() {", + " return {", + " type: 'IntervalValue',", + " a: this.a,", + " b: this.b,", + " };", + " }", + " static fromJSON(json) {", + " return new IntervalValue(json.a, json.b);", + " }", + " clone() {", + " return new IntervalValue(this.a, this.b);", + " }", + "}", + "", + "class PiecewiseFunction {", + " constructor() {", + " this.functions = new Array();", + " }", + " findFunction(t) {", + " let mid = 0;", + " let left = 0, right = this.functions.length - 1;", + " while (left + 1 < right) {", + " mid = Math.floor((left + right) / 2);", + " if (t < this.getStartX(mid))", + " right = mid - 1;", + " else if (t > this.getEndX(mid))", + " left = mid + 1;", + " else", + " return mid;", + " }", + " for (let i = left; i <= right; i++) {", + " if (t >= this.functions[i][1] && t <= this.getEndX(i))", + " return i;", + " }", + " return -1;", + " }", + " getStartX(index) {", + " return this.functions[index][1];", + " }", + " setStartX(index, x) {", + " if (index > 0)", + " this.functions[index][1] = x;", + " }", + " getEndX(index) {", + " if (index + 1 < this.functions.length)", + " return this.functions[index + 1][1];", + " return 1;", + " }", + " setEndX(index, x) {", + " if (index + 1 < this.functions.length)", + " this.functions[index + 1][1] = x;", + " }", + " insertFunction(t, func) {", + " const index = this.findFunction(t);", + " this.functions.splice(index + 1, 0, [func, t]);", + " }", + " removeFunction(index) {", + " return this.functions.splice(index, 1)[0][0];", + " }", + " getFunction(index) {", + " return this.functions[index][0];", + " }", + " setFunction(index, func) {", + " this.functions[index][0] = func;", + " }", + " get numOfFunctions() {", + " return this.functions.length;", + " }", + "}", + "", + "class PiecewiseBezier extends PiecewiseFunction {", + " constructor(curves = [[new Bezier(0, 1.0 / 3, 1.0 / 3 * 2, 1), 0]]) {", + " super();", + " this.type = \"function\";", + " this.functions = curves;", + " }", + " genValue(t = 0) {", + " const index = this.findFunction(t);", + " if (index === -1) {", + " return 0;", + " }", + " return this.functions[index][0].genValue((t - this.getStartX(index)) / (this.getEndX(index) - this.getStartX(index)));", + " }", + " toSVG(length, segments) {", + " if (segments < 1)", + " return \"\";", + " let result = [\"M\", 0, this.functions[0][0].p[0]].join(\" \");", + " for (let i = 1.0 / segments; i <= 1; i += 1.0 / segments) {", + " result = [result, \"L\", i * length, this.genValue(i)].join(\" \");", + " }", + " return result;", + " }", + " toJSON() {", + " return {", + " type: \"PiecewiseBezier\",", + " functions: this.functions.map(([bezier, start]) => ({ function: bezier.toJSON(), start: start })),", + " };", + " }", + " static fromJSON(json) {", + " return new PiecewiseBezier(json.functions.map((piecewiseFunction) => ([Bezier.fromJSON(piecewiseFunction.function), piecewiseFunction.start])));", + " }", + " clone() {", + " return new PiecewiseBezier(this.functions.map(([bezier, start]) => ([bezier.clone(), start])));", + " }", + "}", + "", + "function ValueGeneratorFromJSON(json) {", + " switch (json.type) {", + " case 'ConstantValue':", + " return ConstantValue.fromJSON(json);", + " case 'IntervalValue':", + " return IntervalValue.fromJSON(json);", + " case 'PiecewiseBezier':", + " return PiecewiseBezier.fromJSON(json);", + " default:", + " return new ConstantValue(0);", + " }", + "}", + "", + "class RandomQuatGenerator {", + " constructor() {", + " this.type = \"rotation\";", + " }", + " genValue(quat, t) {", + " let x, y, z, u, v, w;", + " do {", + " x = Math.random() * 2 - 1;", + " y = Math.random() * 2 - 1;", + " z = x * x + y * y;", + " } while (z > 1);", + " do {", + " u = Math.random() * 2 - 1;", + " v = Math.random() * 2 - 1;", + " w = u * u + v * v;", + " } while (w > 1);", + " const s = Math.sqrt((1 - z) / w);", + " quat.set(x, y, s * u, s * v);", + " return quat;", + " }", + " toJSON() {", + " return {", + " type: \"RandomQuat\"", + " };", + " }", + " static fromJSON(json) {", + " return new RandomQuatGenerator();", + " }", + " clone() {", + " return new RandomQuatGenerator();", + " }", + "}", + "", + "class AxisAngleGenerator {", + " constructor(axis, angle) {", + " this.axis = axis;", + " this.angle = angle;", + " this.type = 'rotation';", + " }", + " genValue(quat, t) {", + " return quat.setFromAxisAngle(this.axis, this.angle.genValue(t));", + " }", + " toJSON() {", + " return {", + " type: 'AxisAngle',", + " axis: { x: this.axis.x, y: this.axis.y, z: this.axis.z },", + " angle: this.angle.toJSON(),", + " };", + " }", + " static fromJSON(json) {", + " return new AxisAngleGenerator(new THREE.Vector3(json.axis.x, json.axis.y, json.axis.z), ValueGeneratorFromJSON(json.angle));", + " }", + " clone() {", + " return new AxisAngleGenerator(this.axis.clone(), this.angle.clone());", + " }", + "}", + "", + "class EulerGenerator {", + " constructor(angleX, angleY, angleZ, eulerOrder) {", + " this.angleX = angleX;", + " this.angleY = angleY;", + " this.angleZ = angleZ;", + " this.type = 'rotation';", + " this.eular = new THREE.Euler(0, 0, 0, eulerOrder);", + " }", + " genValue(quat, t) {", + " this.eular.set(this.angleX.genValue(t), this.angleY.genValue(t), this.angleZ.genValue(t));", + " return quat.setFromEuler(this.eular);", + " }", + " toJSON() {", + " return {", + " type: 'Euler',", + " angleX: this.angleX.toJSON(),", + " angleY: this.angleY.toJSON(),", + " angleZ: this.angleZ.toJSON(),", + " eulerOrder: this.eular.order,", + " };", + " }", + " static fromJSON(json) {", + " return new EulerGenerator(ValueGeneratorFromJSON(json.angleX), ValueGeneratorFromJSON(json.angleY), ValueGeneratorFromJSON(json.angleZ), json.eulerOrder);", + " }", + " clone() {", + " return new EulerGenerator(this.angleX, this.angleY, this.angleZ, this.eular.order);", + " }", + "}", + "", + "function RotationGeneratorFromJSON(json) {", + " switch (json.type) {", + " case 'AxisAngle':", + " return AxisAngleGenerator.fromJSON(json);", + " case 'Euler':", + " return EulerGenerator.fromJSON(json);", + " case 'RandomQuat':", + " return RandomQuatGenerator.fromJSON(json);", + " default:", + " return new RandomQuatGenerator();", + " }", + "}", + "", + "function GeneratorFromJSON(json) {", + " switch (json.type) {", + " case 'ConstantValue':", + " case 'IntervalValue':", + " case 'PiecewiseBezier':", + " return ValueGeneratorFromJSON(json);", + " case 'AxisAngle':", + " case 'RandomQuat':", + " case 'Euler':", + " return RotationGeneratorFromJSON(json);", + " default:", + " return new ConstantValue(0);", + " }", + "}", + "", + "class ColorOverLife {", + " constructor(color) {", + " this.color = color;", + " this.type = 'ColorOverLife';", + " }", + " initialize(particle) {", + " if (this.color.type === 'memorizedFunction') {", + " particle.colorOverLifeMemory = {};", + " this.color.startGen(particle.colorOverLifeMemory);", + " }", + " }", + " update(particle, delta) {", + " if (this.color.type === 'memorizedFunction') {", + " this.color.genColor(particle.color, particle.age / particle.life, particle.colorOverLifeMemory);", + " }", + " else {", + " this.color.genColor(particle.color, particle.age / particle.life);", + " }", + " particle.color.x *= particle.startColor.x;", + " particle.color.y *= particle.startColor.y;", + " particle.color.z *= particle.startColor.z;", + " particle.color.w *= particle.startColor.w;", + " }", + " frameUpdate(delta) { }", + " toJSON() {", + " return {", + " type: this.type,", + " color: this.color.toJSON(),", + " };", + " }", + " static fromJSON(json) {", + " return new ColorOverLife(ColorGeneratorFromJSON(json.color));", + " }", + " clone() {", + " return new ColorOverLife(this.color.clone());", + " }", + " reset() { }", + "}", + "", + "class RotationOverLife {", + " constructor(angularVelocity) {", + " this.angularVelocity = angularVelocity;", + " this.type = 'RotationOverLife';", + " this.dynamic = !(angularVelocity instanceof ConstantValue || angularVelocity instanceof IntervalValue);", + " }", + " initialize(particle) {", + " this.dynamic = !(this.angularVelocity instanceof ConstantValue || this.angularVelocity instanceof IntervalValue);", + " if (!this.dynamic && typeof particle.rotation === 'number') {", + " particle.angularVelocity = this.angularVelocity.genValue();", + " }", + " }", + " update(particle, delta) {", + " if (!this.dynamic) {", + " if (typeof particle.rotation === 'number') {", + " particle.rotation += delta * particle.angularVelocity;", + " }", + " }", + " else {", + " if (typeof particle.rotation === 'number') {", + " particle.rotation +=", + " delta * this.angularVelocity.genValue(particle.age / particle.life);", + " }", + " }", + " }", + " toJSON() {", + " return {", + " type: this.type,", + " angularVelocity: this.angularVelocity.toJSON(),", + " };", + " }", + " static fromJSON(json) {", + " return new RotationOverLife(ValueGeneratorFromJSON(json.angularVelocity));", + " }", + " frameUpdate(delta) { }", + " clone() {", + " return new RotationOverLife(this.angularVelocity.clone());", + " }", + " reset() { }", + "}", + "", + "const IdentityQuaternion = new THREE.Quaternion();", + "class Rotation3DOverLife {", + " constructor(angularVelocity) {", + " this.angularVelocity = angularVelocity;", + " this.type = 'Rotation3DOverLife';", + " this.tempQuat = new THREE.Quaternion();", + " this.dynamic = !(angularVelocity instanceof RandomQuatGenerator);", + " }", + " initialize(particle) {", + " this.dynamic = !(this.angularVelocity instanceof RandomQuatGenerator);", + " if (particle.rotation instanceof THREE.Quaternion) {", + " particle.angularVelocity = new THREE.Quaternion();", + " this.angularVelocity.genValue(particle.angularVelocity);", + " }", + " }", + " update(particle, delta) {", + " if (particle.rotation instanceof THREE.Quaternion) {", + " if (!this.dynamic) {", + " this.tempQuat.slerpQuaternions(IdentityQuaternion, particle.angularVelocity, delta);", + " particle.rotation.multiply(this.tempQuat);", + " }", + " else {", + " this.angularVelocity.genValue(this.tempQuat, particle.age / particle.life);", + " this.tempQuat.slerpQuaternions(IdentityQuaternion, this.tempQuat, delta);", + " particle.rotation.multiply(this.tempQuat);", + " }", + " }", + " }", + " toJSON() {", + " return {", + " type: this.type,", + " angularVelocity: this.angularVelocity.toJSON(),", + " };", + " }", + " static fromJSON(json) {", + " return new Rotation3DOverLife(RotationGeneratorFromJSON(json.angularVelocity));", + " }", + " frameUpdate(delta) { }", + " clone() {", + " return new Rotation3DOverLife(this.angularVelocity.clone());", + " }", + " reset() { }", + "}", + "", + "class ForceOverLife {", + " initialize(particle) { }", + " constructor(x, y, z) {", + " this.x = x;", + " this.y = y;", + " this.z = z;", + " this.type = 'ForceOverLife';", + " this._temp = new THREE.Vector3();", + " }", + " update(particle, delta) {", + " this._temp.set(this.x.genValue(particle.age / particle.life), this.y.genValue(particle.age / particle.life), this.z.genValue(particle.age / particle.life));", + " particle.velocity.addScaledVector(this._temp, delta);", + " }", + " toJSON() {", + " return {", + " type: this.type,", + " x: this.x.toJSON(),", + " y: this.y.toJSON(),", + " z: this.z.toJSON(),", + " };", + " }", + " static fromJSON(json) {", + " return new ForceOverLife(ValueGeneratorFromJSON(json.x), ValueGeneratorFromJSON(json.y), ValueGeneratorFromJSON(json.z));", + " }", + " frameUpdate(delta) { }", + " clone() {", + " return new ForceOverLife(this.x.clone(), this.y.clone(), this.z.clone());", + " }", + " reset() { }", + "}", + "", + "class SizeOverLife {", + " initialize(particle) {", + " }", + " constructor(size) {", + " this.size = size;", + " this.type = 'SizeOverLife';", + " }", + " update(particle) {", + " particle.size = particle.startSize * this.size.genValue(particle.age / particle.life);", + " }", + " toJSON() {", + " return {", + " type: this.type,", + " size: this.size.toJSON(),", + " };", + " }", + " static fromJSON(json) {", + " return new SizeOverLife(ValueGeneratorFromJSON(json.size));", + " }", + " frameUpdate(delta) {", + " }", + " clone() {", + " return new SizeOverLife(this.size.clone());", + " }", + " reset() {", + " }", + "}", + "", + "class SpeedOverLife {", + " initialize(particle) {", + " }", + " constructor(speed) {", + " this.speed = speed;", + " this.type = 'SpeedOverLife';", + " }", + " update(particle) {", + " particle.speedModifier = this.speed.genValue(particle.age / particle.life);", + " }", + " toJSON() {", + " return {", + " type: this.type,", + " speed: this.speed.toJSON(),", + " };", + " }", + " static fromJSON(json) {", + " return new SpeedOverLife(ValueGeneratorFromJSON(json.speed));", + " }", + " frameUpdate(delta) {", + " }", + " clone() {", + " return new SpeedOverLife(this.speed.clone());", + " }", + " reset() {", + " }", + "}", + "", + "class FrameOverLife {", + " constructor(frame) {", + " this.frame = frame;", + " this.type = 'FrameOverLife';", + " }", + " initialize(particle) {", + " if (!(this.frame instanceof PiecewiseBezier)) {", + " particle.uvTile = Math.floor(this.frame.genValue(0) + 0.001);", + " }", + " }", + " update(particle, delta) {", + " if (this.frame instanceof PiecewiseBezier) {", + " particle.uvTile = Math.floor(this.frame.genValue(particle.age / particle.life) + 0.001);", + " }", + " }", + " frameUpdate(delta) { }", + " toJSON() {", + " return {", + " type: this.type,", + " frame: this.frame.toJSON(),", + " };", + " }", + " static fromJSON(json) {", + " return new FrameOverLife(ValueGeneratorFromJSON(json.frame));", + " }", + " clone() {", + " return new FrameOverLife(this.frame.clone());", + " }", + " reset() { }", + "}", + "", + "new THREE.Vector3(0, 0, 1);", + "class OrbitOverLife {", + " constructor(orbitSpeed, axis = new THREE.Vector3(0, 1, 0)) {", + " this.orbitSpeed = orbitSpeed;", + " this.axis = axis;", + " this.type = 'OrbitOverLife';", + " this.temp = new THREE.Vector3();", + " this.rotation = new THREE.Quaternion();", + " this.line = new THREE.Line3();", + " }", + " initialize(particle) {", + " }", + " update(particle, delta) {", + " this.line.set(new THREE.Vector3(0, 0, 0), this.axis);", + " this.line.closestPointToPoint(particle.position, false, this.temp);", + " this.rotation.setFromAxisAngle(this.axis, this.orbitSpeed.genValue(particle.age / particle.life) * delta);", + " particle.position.sub(this.temp);", + " particle.position.applyQuaternion(this.rotation);", + " particle.position.add(this.temp);", + " }", + " frameUpdate(delta) {", + " }", + " toJSON() {", + " return {", + " type: this.type,", + " orbitSpeed: this.orbitSpeed.toJSON(),", + " axis: [this.axis.x, this.axis.y, this.axis.z],", + " };", + " }", + " static fromJSON(json) {", + " return new OrbitOverLife(ValueGeneratorFromJSON(json.orbitSpeed), json.axis ? new THREE.Vector3(json.axis[0], json.axis[1], json.axis[2]) : undefined);", + " }", + " clone() {", + " return new OrbitOverLife(this.orbitSpeed.clone());", + " }", + " reset() {", + " }", + "}", + "", + "class WidthOverLength {", + " initialize(particle) {", + " }", + " constructor(width) {", + " this.width = width;", + " this.type = 'WidthOverLength';", + " }", + " update(particle) {", + " if (particle instanceof TrailParticle) {", + " const iter = particle.previous.values();", + " for (let i = 0; i < particle.previous.length; i++) {", + " const cur = iter.next();", + " // We added a multiplication with size because it's more convenient for the extension.", + " cur.value.size = this.width.genValue((particle.previous.length - i) / particle.length) * particle.size;", + " }", + " }", + " }", + " frameUpdate(delta) {", + " }", + " toJSON() {", + " return {", + " type: this.type,", + " width: this.width.toJSON(),", + " };", + " }", + " static fromJSON(json) {", + " return new WidthOverLength(ValueGeneratorFromJSON(json.width));", + " }", + " clone() {", + " return new WidthOverLength(this.width.clone());", + " }", + " reset() {", + " }", + "}", + "", + "class ApplyForce {", + " constructor(direction, magnitude) {", + " this.direction = direction;", + " this.magnitude = magnitude;", + " this.type = 'ApplyForce';", + " this.magnitudeValue = this.magnitude.genValue();", + " }", + " initialize(particle) {", + " }", + " update(particle, delta) {", + " particle.velocity.addScaledVector(this.direction, this.magnitudeValue * delta);", + " }", + " frameUpdate(delta) {", + " this.magnitudeValue = this.magnitude.genValue();", + " }", + " toJSON() {", + " return {", + " type: this.type,", + " direction: [this.direction.x, this.direction.y, this.direction.z],", + " magnitude: this.magnitude.toJSON(),", + " };", + " }", + " static fromJSON(json) {", + " var _a;", + " return new ApplyForce(new THREE.Vector3(json.direction[0], json.direction[1], json.direction[2]), ValueGeneratorFromJSON((_a = json.magnitude) !== null && _a !== void 0 ? _a : json.force));", + " }", + " clone() {", + " return new ApplyForce(this.direction.clone(), this.magnitude.clone());", + " }", + " reset() {", + " }", + "}", + "", + "class GravityForce {", + " constructor(center, magnitude) {", + " this.center = center;", + " this.magnitude = magnitude;", + " this.type = 'GravityForce';", + " this.temp = new THREE.Vector3();", + " }", + " initialize(particle) {", + " }", + " update(particle, delta) {", + " this.temp.copy(this.center).sub(particle.position).normalize();", + " particle.velocity.addScaledVector(this.temp, this.magnitude / particle.position.distanceToSquared(this.center) * delta);", + " }", + " frameUpdate(delta) {", + " }", + " toJSON() {", + " return {", + " type: this.type,", + " center: [this.center.x, this.center.y, this.center.z],", + " magnitude: this.magnitude,", + " };", + " }", + " static fromJSON(json) {", + " return new GravityForce(new THREE.Vector3(json.center[0], json.center[1], json.center[2]), json.magnitude);", + " }", + " clone() {", + " return new GravityForce(this.center.clone(), this.magnitude);", + " }", + " reset() {", + " }", + "}", + "", + "new THREE.Vector3(0, 0, 1);", + "class ChangeEmitDirection {", + " constructor(angle) {", + " this.angle = angle;", + " this.type = 'ChangeEmitDirection';", + " this._temp = new THREE.Vector3();", + " this._q = new THREE.Quaternion();", + " }", + " initialize(particle) {", + " const len = particle.velocity.length();", + " if (len == 0)", + " return;", + " particle.velocity.normalize();", + " if (particle.velocity.x === 0 && particle.velocity.y === 0) {", + " this._temp.set(0, particle.velocity.z, 0);", + " }", + " else {", + " this._temp.set(-particle.velocity.y, particle.velocity.x, 0);", + " }", + " this._q.setFromAxisAngle(this._temp.normalize(), this.angle.genValue());", + " this._temp.copy(particle.velocity);", + " particle.velocity.applyQuaternion(this._q);", + " this._q.setFromAxisAngle(this._temp, Math.random() * Math.PI * 2);", + " particle.velocity.applyQuaternion(this._q);", + " particle.velocity.setLength(len);", + " }", + " update(particle, delta) {", + " }", + " frameUpdate(delta) {", + " }", + " toJSON() {", + " return {", + " type: this.type,", + " angle: this.angle.toJSON(),", + " };", + " }", + " static fromJSON(json) {", + " return new ChangeEmitDirection(ValueGeneratorFromJSON(json.angle));", + " }", + " clone() {", + " return new ChangeEmitDirection(this.angle);", + " }", + " reset() {", + " }", + "}", + "", + "const VECTOR_ONE = new THREE.Vector3(1, 1, 1);", + "const VECTOR_Z = new THREE.Vector3(0, 0, 1);", + "let SubParticleEmitMode = void 0;", + "(function (SubParticleEmitMode) {", + " SubParticleEmitMode[SubParticleEmitMode[\"Death\"] = 0] = \"Death\";", + " SubParticleEmitMode[SubParticleEmitMode[\"Birth\"] = 1] = \"Birth\";", + " SubParticleEmitMode[SubParticleEmitMode[\"Frame\"] = 2] = \"Frame\";", + "})(SubParticleEmitMode || (SubParticleEmitMode = {}));", + "class EmitSubParticleSystem {", + " constructor(particleSystem, useVelocityAsBasis, subParticleSystem, mode = SubParticleEmitMode.Frame, emitProbability = 1) {", + " this.particleSystem = particleSystem;", + " this.useVelocityAsBasis = useVelocityAsBasis;", + " this.subParticleSystem = subParticleSystem;", + " this.mode = mode;", + " this.emitProbability = emitProbability;", + " this.type = 'EmitSubParticleSystem';", + " this.q_ = new THREE.Quaternion();", + " this.v_ = new THREE.Vector3();", + " this.v2_ = new THREE.Vector3();", + " this.subEmissions = new Array();", + " if (this.subParticleSystem && this.subParticleSystem.system) {", + " this.subParticleSystem.system.onlyUsedByOther = true;", + " }", + " }", + " initialize(particle) {", + " }", + " update(particle, delta) {", + " if (this.mode === SubParticleEmitMode.Frame) {", + " this.emit(particle, delta);", + " }", + " else if (this.mode === SubParticleEmitMode.Birth && particle.age === 0) {", + " this.emit(particle, delta);", + " }", + " else if (this.mode === SubParticleEmitMode.Death && particle.age + delta >= particle.life) {", + " this.emit(particle, delta);", + " }", + " }", + " emit(particle, delta) {", + " if (!this.subParticleSystem)", + " return;", + " if (Math.random() > this.emitProbability) {", + " return;", + " }", + " const m = new THREE.Matrix4();", + " this.setMatrixFromParticle(m, particle);", + " this.subEmissions.push({", + " burstIndex: 0,", + " burstWaveIndex: 0,", + " time: 0,", + " waitEmiting: 0,", + " matrix: m,", + " travelDistance: 0,", + " particle: particle,", + " });", + " }", + " frameUpdate(delta) {", + " if (!this.subParticleSystem)", + " return;", + " for (let i = 0; i < this.subEmissions.length; i++) {", + " if (this.subEmissions[i].time >= this.subParticleSystem.system.duration) {", + " this.subEmissions[i] = this.subEmissions[this.subEmissions.length - 1];", + " this.subEmissions.length = this.subEmissions.length - 1;", + " i--;", + " }", + " else {", + " let subEmissionState = this.subEmissions[i];", + " if (subEmissionState.particle && subEmissionState.particle.age < subEmissionState.particle.life) {", + " this.setMatrixFromParticle(subEmissionState.matrix, subEmissionState.particle);", + " }", + " else {", + " subEmissionState.particle = undefined;", + " }", + " this.subParticleSystem.system.emit(delta, subEmissionState, subEmissionState.matrix);", + " }", + " }", + " }", + " toJSON() {", + " return {", + " type: this.type,", + " subParticleSystem: this.subParticleSystem ? this.subParticleSystem.uuid : '',", + " useVelocityAsBasis: this.useVelocityAsBasis,", + " mode: this.mode,", + " emitProbability: this.emitProbability,", + " };", + " }", + " static fromJSON(json, particleSystem) {", + " return new EmitSubParticleSystem(particleSystem, json.useVelocityAsBasis, json.subParticleSystem, json.mode, json.emitProbability);", + " }", + " clone() {", + " return new EmitSubParticleSystem(this.particleSystem, this.useVelocityAsBasis, this.subParticleSystem, this.mode, this.emitProbability);", + " }", + " reset() { }", + " setMatrixFromParticle(m, particle) {", + " let rotation;", + " if (particle.rotation === undefined || this.useVelocityAsBasis) {", + " if (particle.velocity.x === 0 &&", + " particle.velocity.y === 0 &&", + " (particle.velocity.z === 1 || particle.velocity.z === 0)) {", + " m.set(1, 0, 0, particle.position.x, 0, 1, 0, particle.position.y, 0, 0, 1, particle.position.z, 0, 0, 0, 1);", + " }", + " else {", + " this.v_.copy(VECTOR_Z).cross(particle.velocity);", + " this.v2_.copy(particle.velocity).cross(this.v_);", + " const len = this.v_.length();", + " const len2 = this.v2_.length();", + " m.set(this.v_.x / len, this.v2_.x / len2, particle.velocity.x, particle.position.x, this.v_.y / len, this.v2_.y / len2, particle.velocity.y, particle.position.y, this.v_.z / len, this.v2_.z / len2, particle.velocity.z, particle.position.z, 0, 0, 0, 1);", + " }", + " }", + " else {", + " if (particle.rotation instanceof THREE.Quaternion) {", + " rotation = particle.rotation;", + " }", + " else {", + " this.q_.setFromAxisAngle(VECTOR_Z, particle.rotation);", + " rotation = this.q_;", + " }", + " m.compose(particle.position, rotation, VECTOR_ONE);", + " }", + " if (!this.particleSystem.worldSpace) {", + " m.multiplyMatrices(this.particleSystem.emitter.matrixWorld, m);", + " }", + " }", + "}", + "", + "const F2 = 0.5 * (Math.sqrt(3.0) - 1.0);", + "const G2 = (3.0 - Math.sqrt(3.0)) / 6.0;", + "const F3 = 1.0 / 3.0;", + "const G3 = 1.0 / 6.0;", + "const F4 = (Math.sqrt(5.0) - 1.0) / 4.0;", + "const G4 = (5.0 - Math.sqrt(5.0)) / 20.0;", + "const grad3 = new Float32Array([1, 1, 0,", + " -1, 1, 0,", + " 1, -1, 0,", + " -1, -1, 0,", + " 1, 0, 1,", + " -1, 0, 1,", + " 1, 0, -1,", + " -1, 0, -1,", + " 0, 1, 1,", + " 0, -1, 1,", + " 0, 1, -1,", + " 0, -1, -1]);", + "const grad4 = new Float32Array([0, 1, 1, 1, 0, 1, 1, -1, 0, 1, -1, 1, 0, 1, -1, -1,", + " 0, -1, 1, 1, 0, -1, 1, -1, 0, -1, -1, 1, 0, -1, -1, -1,", + " 1, 0, 1, 1, 1, 0, 1, -1, 1, 0, -1, 1, 1, 0, -1, -1,", + " -1, 0, 1, 1, -1, 0, 1, -1, -1, 0, -1, 1, -1, 0, -1, -1,", + " 1, 1, 0, 1, 1, 1, 0, -1, 1, -1, 0, 1, 1, -1, 0, -1,", + " -1, 1, 0, 1, -1, 1, 0, -1, -1, -1, 0, 1, -1, -1, 0, -1,", + " 1, 1, 1, 0, 1, 1, -1, 0, 1, -1, 1, 0, 1, -1, -1, 0,", + " -1, 1, 1, 0, -1, 1, -1, 0, -1, -1, 1, 0, -1, -1, -1, 0]);", + "class SimplexNoise {", + " constructor(randomOrSeed = Math.random) {", + " const random = typeof randomOrSeed == 'function' ? randomOrSeed : alea(randomOrSeed);", + " this.p = buildPermutationTable(random);", + " this.perm = new Uint8Array(512);", + " this.permMod12 = new Uint8Array(512);", + " for (let i = 0; i < 512; i++) {", + " this.perm[i] = this.p[i & 255];", + " this.permMod12[i] = this.perm[i] % 12;", + " }", + " }", + " noise2D(x, y) {", + " const permMod12 = this.permMod12;", + " const perm = this.perm;", + " let n0 = 0;", + " let n1 = 0;", + " let n2 = 0;", + " const s = (x + y) * F2;", + " const i = Math.floor(x + s);", + " const j = Math.floor(y + s);", + " const t = (i + j) * G2;", + " const X0 = i - t;", + " const Y0 = j - t;", + " const x0 = x - X0;", + " const y0 = y - Y0;", + " let i1, j1;", + " if (x0 > y0) {", + " i1 = 1;", + " j1 = 0;", + " }", + " else {", + " i1 = 0;", + " j1 = 1;", + " }", + " const x1 = x0 - i1 + G2;", + " const y1 = y0 - j1 + G2;", + " const x2 = x0 - 1.0 + 2.0 * G2;", + " const y2 = y0 - 1.0 + 2.0 * G2;", + " const ii = i & 255;", + " const jj = j & 255;", + " let t0 = 0.5 - x0 * x0 - y0 * y0;", + " if (t0 >= 0) {", + " const gi0 = permMod12[ii + perm[jj]] * 3;", + " t0 *= t0;", + " n0 = t0 * t0 * (grad3[gi0] * x0 + grad3[gi0 + 1] * y0);", + " }", + " let t1 = 0.5 - x1 * x1 - y1 * y1;", + " if (t1 >= 0) {", + " const gi1 = permMod12[ii + i1 + perm[jj + j1]] * 3;", + " t1 *= t1;", + " n1 = t1 * t1 * (grad3[gi1] * x1 + grad3[gi1 + 1] * y1);", + " }", + " let t2 = 0.5 - x2 * x2 - y2 * y2;", + " if (t2 >= 0) {", + " const gi2 = permMod12[ii + 1 + perm[jj + 1]] * 3;", + " t2 *= t2;", + " n2 = t2 * t2 * (grad3[gi2] * x2 + grad3[gi2 + 1] * y2);", + " }", + " return 70.0 * (n0 + n1 + n2);", + " }", + " noise3D(x, y, z) {", + " const permMod12 = this.permMod12;", + " const perm = this.perm;", + " let n0, n1, n2, n3;", + " const s = (x + y + z) * F3;", + " const i = Math.floor(x + s);", + " const j = Math.floor(y + s);", + " const k = Math.floor(z + s);", + " const t = (i + j + k) * G3;", + " const X0 = i - t;", + " const Y0 = j - t;", + " const Z0 = k - t;", + " const x0 = x - X0;", + " const y0 = y - Y0;", + " const z0 = z - Z0;", + " let i1, j1, k1;", + " let i2, j2, k2;", + " if (x0 >= y0) {", + " if (y0 >= z0) {", + " i1 = 1;", + " j1 = 0;", + " k1 = 0;", + " i2 = 1;", + " j2 = 1;", + " k2 = 0;", + " }", + " else if (x0 >= z0) {", + " i1 = 1;", + " j1 = 0;", + " k1 = 0;", + " i2 = 1;", + " j2 = 0;", + " k2 = 1;", + " }", + " else {", + " i1 = 0;", + " j1 = 0;", + " k1 = 1;", + " i2 = 1;", + " j2 = 0;", + " k2 = 1;", + " }", + " }", + " else {", + " if (y0 < z0) {", + " i1 = 0;", + " j1 = 0;", + " k1 = 1;", + " i2 = 0;", + " j2 = 1;", + " k2 = 1;", + " }", + " else if (x0 < z0) {", + " i1 = 0;", + " j1 = 1;", + " k1 = 0;", + " i2 = 0;", + " j2 = 1;", + " k2 = 1;", + " }", + " else {", + " i1 = 0;", + " j1 = 1;", + " k1 = 0;", + " i2 = 1;", + " j2 = 1;", + " k2 = 0;", + " }", + " }", + " const x1 = x0 - i1 + G3;", + " const y1 = y0 - j1 + G3;", + " const z1 = z0 - k1 + G3;", + " const x2 = x0 - i2 + 2.0 * G3;", + " const y2 = y0 - j2 + 2.0 * G3;", + " const z2 = z0 - k2 + 2.0 * G3;", + " const x3 = x0 - 1.0 + 3.0 * G3;", + " const y3 = y0 - 1.0 + 3.0 * G3;", + " const z3 = z0 - 1.0 + 3.0 * G3;", + " const ii = i & 255;", + " const jj = j & 255;", + " const kk = k & 255;", + " let t0 = 0.6 - x0 * x0 - y0 * y0 - z0 * z0;", + " if (t0 < 0)", + " n0 = 0.0;", + " else {", + " const gi0 = permMod12[ii + perm[jj + perm[kk]]] * 3;", + " t0 *= t0;", + " n0 = t0 * t0 * (grad3[gi0] * x0 + grad3[gi0 + 1] * y0 + grad3[gi0 + 2] * z0);", + " }", + " let t1 = 0.6 - x1 * x1 - y1 * y1 - z1 * z1;", + " if (t1 < 0)", + " n1 = 0.0;", + " else {", + " const gi1 = permMod12[ii + i1 + perm[jj + j1 + perm[kk + k1]]] * 3;", + " t1 *= t1;", + " n1 = t1 * t1 * (grad3[gi1] * x1 + grad3[gi1 + 1] * y1 + grad3[gi1 + 2] * z1);", + " }", + " let t2 = 0.6 - x2 * x2 - y2 * y2 - z2 * z2;", + " if (t2 < 0)", + " n2 = 0.0;", + " else {", + " const gi2 = permMod12[ii + i2 + perm[jj + j2 + perm[kk + k2]]] * 3;", + " t2 *= t2;", + " n2 = t2 * t2 * (grad3[gi2] * x2 + grad3[gi2 + 1] * y2 + grad3[gi2 + 2] * z2);", + " }", + " let t3 = 0.6 - x3 * x3 - y3 * y3 - z3 * z3;", + " if (t3 < 0)", + " n3 = 0.0;", + " else {", + " const gi3 = permMod12[ii + 1 + perm[jj + 1 + perm[kk + 1]]] * 3;", + " t3 *= t3;", + " n3 = t3 * t3 * (grad3[gi3] * x3 + grad3[gi3 + 1] * y3 + grad3[gi3 + 2] * z3);", + " }", + " return 32.0 * (n0 + n1 + n2 + n3);", + " }", + " noise4D(x, y, z, w) {", + " const perm = this.perm;", + " let n0, n1, n2, n3, n4;", + " const s = (x + y + z + w) * F4;", + " const i = Math.floor(x + s);", + " const j = Math.floor(y + s);", + " const k = Math.floor(z + s);", + " const l = Math.floor(w + s);", + " const t = (i + j + k + l) * G4;", + " const X0 = i - t;", + " const Y0 = j - t;", + " const Z0 = k - t;", + " const W0 = l - t;", + " const x0 = x - X0;", + " const y0 = y - Y0;", + " const z0 = z - Z0;", + " const w0 = w - W0;", + " let rankx = 0;", + " let ranky = 0;", + " let rankz = 0;", + " let rankw = 0;", + " if (x0 > y0)", + " rankx++;", + " else", + " ranky++;", + " if (x0 > z0)", + " rankx++;", + " else", + " rankz++;", + " if (x0 > w0)", + " rankx++;", + " else", + " rankw++;", + " if (y0 > z0)", + " ranky++;", + " else", + " rankz++;", + " if (y0 > w0)", + " ranky++;", + " else", + " rankw++;", + " if (z0 > w0)", + " rankz++;", + " else", + " rankw++;", + " const i1 = rankx >= 3 ? 1 : 0;", + " const j1 = ranky >= 3 ? 1 : 0;", + " const k1 = rankz >= 3 ? 1 : 0;", + " const l1 = rankw >= 3 ? 1 : 0;", + " const i2 = rankx >= 2 ? 1 : 0;", + " const j2 = ranky >= 2 ? 1 : 0;", + " const k2 = rankz >= 2 ? 1 : 0;", + " const l2 = rankw >= 2 ? 1 : 0;", + " const i3 = rankx >= 1 ? 1 : 0;", + " const j3 = ranky >= 1 ? 1 : 0;", + " const k3 = rankz >= 1 ? 1 : 0;", + " const l3 = rankw >= 1 ? 1 : 0;", + " const x1 = x0 - i1 + G4;", + " const y1 = y0 - j1 + G4;", + " const z1 = z0 - k1 + G4;", + " const w1 = w0 - l1 + G4;", + " const x2 = x0 - i2 + 2.0 * G4;", + " const y2 = y0 - j2 + 2.0 * G4;", + " const z2 = z0 - k2 + 2.0 * G4;", + " const w2 = w0 - l2 + 2.0 * G4;", + " const x3 = x0 - i3 + 3.0 * G4;", + " const y3 = y0 - j3 + 3.0 * G4;", + " const z3 = z0 - k3 + 3.0 * G4;", + " const w3 = w0 - l3 + 3.0 * G4;", + " const x4 = x0 - 1.0 + 4.0 * G4;", + " const y4 = y0 - 1.0 + 4.0 * G4;", + " const z4 = z0 - 1.0 + 4.0 * G4;", + " const w4 = w0 - 1.0 + 4.0 * G4;", + " const ii = i & 255;", + " const jj = j & 255;", + " const kk = k & 255;", + " const ll = l & 255;", + " let t0 = 0.6 - x0 * x0 - y0 * y0 - z0 * z0 - w0 * w0;", + " if (t0 < 0)", + " n0 = 0.0;", + " else {", + " const gi0 = (perm[ii + perm[jj + perm[kk + perm[ll]]]] % 32) * 4;", + " t0 *= t0;", + " n0 = t0 * t0 * (grad4[gi0] * x0 + grad4[gi0 + 1] * y0 + grad4[gi0 + 2] * z0 + grad4[gi0 + 3] * w0);", + " }", + " let t1 = 0.6 - x1 * x1 - y1 * y1 - z1 * z1 - w1 * w1;", + " if (t1 < 0)", + " n1 = 0.0;", + " else {", + " const gi1 = (perm[ii + i1 + perm[jj + j1 + perm[kk + k1 + perm[ll + l1]]]] % 32) * 4;", + " t1 *= t1;", + " n1 = t1 * t1 * (grad4[gi1] * x1 + grad4[gi1 + 1] * y1 + grad4[gi1 + 2] * z1 + grad4[gi1 + 3] * w1);", + " }", + " let t2 = 0.6 - x2 * x2 - y2 * y2 - z2 * z2 - w2 * w2;", + " if (t2 < 0)", + " n2 = 0.0;", + " else {", + " const gi2 = (perm[ii + i2 + perm[jj + j2 + perm[kk + k2 + perm[ll + l2]]]] % 32) * 4;", + " t2 *= t2;", + " n2 = t2 * t2 * (grad4[gi2] * x2 + grad4[gi2 + 1] * y2 + grad4[gi2 + 2] * z2 + grad4[gi2 + 3] * w2);", + " }", + " let t3 = 0.6 - x3 * x3 - y3 * y3 - z3 * z3 - w3 * w3;", + " if (t3 < 0)", + " n3 = 0.0;", + " else {", + " const gi3 = (perm[ii + i3 + perm[jj + j3 + perm[kk + k3 + perm[ll + l3]]]] % 32) * 4;", + " t3 *= t3;", + " n3 = t3 * t3 * (grad4[gi3] * x3 + grad4[gi3 + 1] * y3 + grad4[gi3 + 2] * z3 + grad4[gi3 + 3] * w3);", + " }", + " let t4 = 0.6 - x4 * x4 - y4 * y4 - z4 * z4 - w4 * w4;", + " if (t4 < 0)", + " n4 = 0.0;", + " else {", + " const gi4 = (perm[ii + 1 + perm[jj + 1 + perm[kk + 1 + perm[ll + 1]]]] % 32) * 4;", + " t4 *= t4;", + " n4 = t4 * t4 * (grad4[gi4] * x4 + grad4[gi4 + 1] * y4 + grad4[gi4 + 2] * z4 + grad4[gi4 + 3] * w4);", + " }", + " return 27.0 * (n0 + n1 + n2 + n3 + n4);", + " }", + "}", + "function buildPermutationTable(random) {", + " const p = new Uint8Array(256);", + " for (let i = 0; i < 256; i++) {", + " p[i] = i;", + " }", + " for (let i = 0; i < 255; i++) {", + " const r = i + ~~(random() * (256 - i));", + " const aux = p[i];", + " p[i] = p[r];", + " p[r] = aux;", + " }", + " return p;", + "}", + "function alea(seed) {", + " let s0 = 0;", + " let s1 = 0;", + " let s2 = 0;", + " let c = 1;", + " const mash = masher();", + " s0 = mash(' ');", + " s1 = mash(' ');", + " s2 = mash(' ');", + " s0 -= mash(seed);", + " if (s0 < 0) {", + " s0 += 1;", + " }", + " s1 -= mash(seed);", + " if (s1 < 0) {", + " s1 += 1;", + " }", + " s2 -= mash(seed);", + " if (s2 < 0) {", + " s2 += 1;", + " }", + " return function () {", + " const t = 2091639 * s0 + c * 2.3283064365386963e-10;", + " s0 = s1;", + " s1 = s2;", + " return s2 = t - (c = t | 0);", + " };", + "}", + "function masher() {", + " let n = 0xefc8249d;", + " return function (data) {", + " data = data.toString();", + " for (let i = 0; i < data.length; i++) {", + " n += data.charCodeAt(i);", + " let h = 0.02519603282416938 * n;", + " n = h >>> 0;", + " h -= n;", + " h *= n;", + " n = h >>> 0;", + " h -= n;", + " n += h * 0x100000000;", + " }", + " return (n >>> 0) * 2.3283064365386963e-10;", + " };", + "}", + "", + "class TurbulenceField {", + " constructor(scale, octaves, velocityMultiplier, timeScale) {", + " this.scale = scale;", + " this.octaves = octaves;", + " this.velocityMultiplier = velocityMultiplier;", + " this.timeScale = timeScale;", + " this.type = 'TurbulenceField';", + " this.generator = new SimplexNoise();", + " this.timeOffset = new THREE.Vector3();", + " this.temp = new THREE.Vector3();", + " this.temp2 = new THREE.Vector3();", + " this.timeOffset.x = Math.random() / this.scale.x * this.timeScale.x;", + " this.timeOffset.y = Math.random() / this.scale.y * this.timeScale.y;", + " this.timeOffset.z = Math.random() / this.scale.z * this.timeScale.z;", + " }", + " initialize(particle) {", + " }", + " update(particle, delta) {", + " const x = particle.position.x / this.scale.x;", + " const y = particle.position.y / this.scale.y;", + " const z = particle.position.z / this.scale.z;", + " this.temp.set(0, 0, 0);", + " let lvl = 1;", + " for (let i = 0; i < this.octaves; i++) {", + " this.temp2.set(this.generator.noise4D(x * lvl, y * lvl, z * lvl, this.timeOffset.x * lvl) / lvl, this.generator.noise4D(x * lvl, y * lvl, z * lvl, this.timeOffset.y * lvl) / lvl, this.generator.noise4D(x * lvl, y * lvl, z * lvl, this.timeOffset.z * lvl) / lvl);", + " this.temp.add(this.temp2);", + " lvl *= 2;", + " }", + " this.temp.multiply(this.velocityMultiplier);", + " particle.velocity.addScaledVector(this.temp, delta);", + " }", + " toJSON() {", + " return {", + " type: this.type,", + " scale: [this.scale.x, this.scale.y, this.scale.z],", + " octaves: this.octaves,", + " velocityMultiplier: [this.velocityMultiplier.x, this.velocityMultiplier.y, this.velocityMultiplier.z],", + " timeScale: [this.timeScale.x, this.timeScale.y, this.timeScale.z],", + " };", + " }", + " frameUpdate(delta) {", + " this.timeOffset.x += delta * this.timeScale.x;", + " this.timeOffset.y += delta * this.timeScale.y;", + " this.timeOffset.z += delta * this.timeScale.z;", + " }", + " static fromJSON(json) {", + " return new TurbulenceField(new THREE.Vector3(json.scale[0], json.scale[1], json.scale[2]), json.octaves, new THREE.Vector3(json.velocityMultiplier[0], json.velocityMultiplier[1], json.velocityMultiplier[2]), new THREE.Vector3(json.timeScale[0], json.timeScale[1], json.timeScale[2]));", + " }", + " clone() {", + " return new TurbulenceField(this.scale.clone(), this.octaves, this.velocityMultiplier.clone(), this.timeScale.clone());", + " }", + " reset() {", + " }", + "}", + "", + "function randomInt(a, b) {", + " return Math.floor(Math.random() * (b - a)) + a;", + "}", + "", + "const generators = [];", + "const tempV$2 = new THREE.Vector3();", + "const tempQ$2 = new THREE.Quaternion();", + "class Noise {", + " constructor(frequency, power, positionAmount = new ConstantValue(1), rotationAmount = new ConstantValue(0)) {", + " this.frequency = frequency;", + " this.power = power;", + " this.positionAmount = positionAmount;", + " this.rotationAmount = rotationAmount;", + " this.type = 'Noise';", + " this.duration = 0;", + " if (generators.length === 0) {", + " for (let i = 0; i < 100; i++) {", + " generators.push(new SimplexNoise());", + " }", + " }", + " }", + " initialize(particle) {", + " particle.lastPosNoise = new THREE.Vector3();", + " if (typeof particle.rotation === 'number') {", + " particle.lastRotNoise = 0;", + " }", + " else {", + " particle.lastRotNoise = new THREE.Quaternion();", + " }", + " particle.generatorIndex = [randomInt(0, 100), randomInt(0, 100), randomInt(0, 100), randomInt(0, 100)];", + " }", + " update(particle, _) {", + " let frequency = this.frequency.genValue(particle.age / particle.life);", + " let power = this.power.genValue(particle.age / particle.life);", + " let positionAmount = this.positionAmount.genValue(particle.age / particle.life);", + " let rotationAmount = this.rotationAmount.genValue(particle.age / particle.life);", + " if (positionAmount > 0 && particle.lastPosNoise !== undefined) {", + " particle.position.sub(particle.lastPosNoise);", + " tempV$2.set(generators[particle.generatorIndex[0]].noise2D(0, particle.age * frequency) *", + " power *", + " positionAmount, generators[particle.generatorIndex[1]].noise2D(0, particle.age * frequency) *", + " power *", + " positionAmount, generators[particle.generatorIndex[2]].noise2D(0, particle.age * frequency) *", + " power *", + " positionAmount);", + " particle.position.add(tempV$2);", + " particle.lastPosNoise.copy(tempV$2);", + " }", + " if (rotationAmount > 0 && particle.lastRotNoise !== undefined) {", + " if (typeof particle.rotation === 'number') {", + " particle.rotation -= particle.lastRotNoise;", + " particle.rotation +=", + " generators[particle.generatorIndex[3]].noise2D(0, particle.age * frequency) *", + " Math.PI *", + " power *", + " rotationAmount;", + " }", + " else {", + " particle.lastRotNoise.invert();", + " particle.rotation.multiply(particle.lastRotNoise);", + " tempQ$2", + " .set(generators[particle.generatorIndex[0]].noise2D(0, particle.age * frequency) *", + " power *", + " rotationAmount, generators[particle.generatorIndex[1]].noise2D(0, particle.age * frequency) *", + " power *", + " rotationAmount, generators[particle.generatorIndex[2]].noise2D(0, particle.age * frequency) *", + " power *", + " rotationAmount, generators[particle.generatorIndex[3]].noise2D(0, particle.age * frequency) *", + " power *", + " rotationAmount)", + " .normalize();", + " particle.rotation.multiply(tempQ$2);", + " particle.lastRotNoise.copy(tempQ$2);", + " }", + " }", + " }", + " toJSON() {", + " return {", + " type: this.type,", + " frequency: this.frequency.toJSON(),", + " power: this.power.toJSON(),", + " positionAmount: this.positionAmount.toJSON(),", + " rotationAmount: this.rotationAmount.toJSON(),", + " };", + " }", + " frameUpdate(delta) {", + " this.duration += delta;", + " }", + " static fromJSON(json) {", + " return new Noise(ValueGeneratorFromJSON(json.frequency), ValueGeneratorFromJSON(json.power), ValueGeneratorFromJSON(json.positionAmount), ValueGeneratorFromJSON(json.rotationAmount));", + " }", + " clone() {", + " return new Noise(this.frequency.clone(), this.power.clone(), this.positionAmount.clone(), this.rotationAmount.clone());", + " }", + " reset() { }", + "}", + "", + "class TextureSequencer {", + " constructor(scaleX = 0, scaleY = 0, position = new THREE.Vector3()) {", + " this.scaleX = scaleX;", + " this.scaleY = scaleY;", + " this.position = position;", + " this.locations = [];", + " }", + " transform(position, index) {", + " position.x = this.locations[index % this.locations.length].x * this.scaleX + this.position.x;", + " position.y = this.locations[index % this.locations.length].y * this.scaleY + this.position.y;", + " position.z = this.position.z;", + " }", + " static fromJSON(json) {", + " const textureSequencer = new TextureSequencer(json.scaleX, json.scaleY, new THREE.Vector3(json.position[0], json.position[1], json.position[2]));", + " textureSequencer.locations = json.locations.map((loc) => new THREE.Vector2(loc.x, loc.y));", + " return textureSequencer;", + " }", + " clone() {", + " const textureSequencer = new TextureSequencer(this.scaleX, this.scaleY, this.position.clone());", + " textureSequencer.locations = this.locations.map(loc => loc.clone());", + " return textureSequencer;", + " }", + " toJSON() {", + " return {", + " scaleX: this.scaleX,", + " scaleY: this.scaleY,", + " position: this.position,", + " locations: this.locations.map(loc => ({", + " x: loc.x,", + " y: loc.y,", + " }))", + " };", + " }", + " fromImage(img, threshold) {", + " const canvas = document.createElement(\"canvas\");", + " canvas.width = img.width;", + " canvas.height = img.height;", + " const ctx = canvas.getContext(\"2d\");", + " if (!ctx) {", + " return;", + " }", + " ctx.drawImage(img, 0, 0);", + " const data = ctx.getImageData(0, 0, canvas.width, canvas.height, { colorSpace: \"srgb\" });", + " canvas.remove();", + " this.locations.length = 0;", + " for (let i = 0; i < data.height; i++) {", + " for (let j = 0; j < data.width; j++) {", + " if (data.data[(i * data.width + j) * 4 + 3] > threshold) {", + " this.locations.push(new THREE.Vector2(j, data.height - i));", + " }", + " }", + " }", + " }", + "}", + "", + "function SequencerFromJSON(json) {", + " switch (json.type) {", + " case 'TextureSequencer':", + " return TextureSequencer.fromJSON(json);", + " default:", + " return new TextureSequencer();", + " }", + "}", + "", + "class ApplySequences {", + " constructor(delayBetweenParticles) {", + " this.type = 'ApplySequences';", + " this.sequencers = [];", + " this.time = 0;", + " this.index = 0;", + " this.pCount = 0;", + " this.tempV = new THREE.Vector3();", + " this.delay = delayBetweenParticles;", + " }", + " initialize(particle) {", + " particle.id = this.pCount;", + " particle.dst = new THREE.Vector3();", + " particle.begin = new THREE.Vector3();", + " particle.inMotion = false;", + " this.pCount++;", + " }", + " reset() {", + " this.time = 0;", + " this.index = 0;", + " this.pCount = 0;", + " }", + " update(particle, delta) {", + " const sequencer = this.sequencers[this.index];", + " const delay = particle.id * this.delay;", + " if (this.time >= sequencer[0].a + delay && this.time <= sequencer[0].b + delay) {", + " if (!particle.inMotion) {", + " particle.inMotion = true;", + " particle.begin.copy(particle.position);", + " sequencer[1].transform(particle.dst, particle.id);", + " }", + " particle.position.lerpVectors(particle.begin, particle.dst, ApplySequences.BEZIER.genValue((this.time - sequencer[0].a - delay) / (sequencer[0].b - sequencer[0].a)));", + " }", + " else if (this.time > sequencer[0].b + delay) {", + " particle.inMotion = false;", + " }", + " }", + " frameUpdate(delta) {", + " while (this.index + 1 < this.sequencers.length && this.time >= this.sequencers[this.index + 1][0].a) {", + " this.index++;", + " }", + " this.time += delta;", + " }", + " appendSequencer(range, sequencer) {", + " this.sequencers.push([range, sequencer]);", + " }", + " toJSON() {", + " return {", + " type: this.type,", + " delay: this.delay,", + " sequencers: this.sequencers.map(([range, sequencer]) => ({", + " range: range.toJSON(),", + " sequencer: sequencer.toJSON(),", + " })),", + " };", + " }", + " static fromJSON(json) {", + " const seq = new ApplySequences(json.delay);", + " json.sequencers.forEach((sequencerJson) => {", + " seq.sequencers.push([ValueGeneratorFromJSON(sequencerJson.range), SequencerFromJSON(sequencerJson.sequencer)]);", + " });", + " return seq;", + " }", + " clone() {", + " const applySequences = new ApplySequences(this.delay);", + " applySequences.sequencers = this.sequencers.map(seq => [seq[0].clone(), seq[1].clone()]);", + " return applySequences;", + " }", + "}", + "ApplySequences.BEZIER = new Bezier(0, 0, 1, 1);", + "", + "let physicsResolver;", + "function setPhysicsResolver(resolver) {", + " physicsResolver = resolver;", + "}", + "function getPhysicsResolver() {", + " return physicsResolver;", + "}", + "class ApplyCollision {", + " constructor(resolver, bounce) {", + " this.resolver = resolver;", + " this.bounce = bounce;", + " this.type = 'ApplyCollision';", + " this.tempV = new THREE.Vector3();", + " }", + " initialize(particle) {", + " }", + " update(particle, delta) {", + " if (this.resolver.resolve(particle.position, this.tempV)) {", + " particle.velocity.reflect(this.tempV).multiplyScalar(this.bounce);", + " }", + " }", + " frameUpdate(delta) {", + " }", + " toJSON() {", + " return {", + " type: this.type,", + " bounce: this.bounce,", + " };", + " }", + " static fromJSON(json) {", + " return new ApplyCollision(getPhysicsResolver(), json.bounce);", + " }", + " clone() {", + " return new ApplyCollision(this.resolver, this.bounce);", + " }", + " reset() {", + " }", + "}", + "", + "class ColorBySpeed {", + " constructor(color, speedRange) {", + " this.color = color;", + " this.speedRange = speedRange;", + " this.type = 'ColorBySpeed';", + " }", + " initialize(particle) {", + " if (this.color.type === 'memorizedFunction') {", + " particle.colorOverLifeMemory = {};", + " this.color.startGen(particle.colorOverLifeMemory);", + " }", + " }", + " update(particle, delta) {", + " const t = (particle.startSpeed - this.speedRange.a) / (this.speedRange.b - this.speedRange.a);", + " if (this.color.type === 'memorizedFunction') {", + " this.color.genColor(particle.color, t, particle.colorOverLifeMemory);", + " }", + " else {", + " this.color.genColor(particle.color, t);", + " }", + " particle.color.x *= particle.startColor.x;", + " particle.color.y *= particle.startColor.y;", + " particle.color.z *= particle.startColor.z;", + " particle.color.w *= particle.startColor.w;", + " }", + " frameUpdate(delta) { }", + " toJSON() {", + " return {", + " type: this.type,", + " color: this.color.toJSON(),", + " speedRange: this.speedRange.toJSON(),", + " };", + " }", + " static fromJSON(json) {", + " return new ColorBySpeed(ColorGeneratorFromJSON(json.color), IntervalValue.fromJSON(json.speedRange));", + " }", + " clone() {", + " return new ColorBySpeed(this.color.clone(), this.speedRange.clone());", + " }", + " reset() { }", + "}", + "", + "class SizeBySpeed {", + " initialize(particle) { }", + " constructor(size, speedRange) {", + " this.size = size;", + " this.speedRange = speedRange;", + " this.type = 'SizeBySpeed';", + " }", + " update(particle) {", + " const t = (particle.startSpeed - this.speedRange.a) / (this.speedRange.b - this.speedRange.a);", + " particle.size = particle.startSize * this.size.genValue(t);", + " }", + " toJSON() {", + " return {", + " type: this.type,", + " size: this.size.toJSON(),", + " speedRange: this.speedRange.toJSON(),", + " };", + " }", + " static fromJSON(json) {", + " return new SizeBySpeed(ValueGeneratorFromJSON(json.size), IntervalValue.fromJSON(json.speedRange));", + " }", + " frameUpdate(delta) { }", + " clone() {", + " return new SizeBySpeed(this.size.clone(), this.speedRange.clone());", + " }", + " reset() { }", + "}", + "", + "class RotationBySpeed {", + " constructor(angularVelocity, speedRange) {", + " this.angularVelocity = angularVelocity;", + " this.speedRange = speedRange;", + " this.type = 'RotationBySpeed';", + " this.tempQuat = new THREE.Quaternion();", + " }", + " initialize(particle) { }", + " update(particle, delta) {", + " const t = (particle.startSpeed - this.speedRange.a) / (this.speedRange.b - this.speedRange.a);", + " particle.rotation += delta * this.angularVelocity.genValue(t);", + " }", + " toJSON() {", + " return {", + " type: this.type,", + " angularVelocity: this.angularVelocity.toJSON(),", + " speedRange: this.speedRange.toJSON(),", + " };", + " }", + " static fromJSON(json) {", + " return new RotationBySpeed(ValueGeneratorFromJSON(json.angularVelocity), IntervalValue.fromJSON(json.speedRange));", + " }", + " frameUpdate(delta) { }", + " clone() {", + " return new RotationBySpeed(this.angularVelocity.clone(), this.speedRange.clone());", + " }", + " reset() { }", + "}", + "", + "class LimitSpeedOverLife {", + " initialize(particle) { }", + " constructor(speed, dampen) {", + " this.speed = speed;", + " this.dampen = dampen;", + " this.type = 'LimitSpeedOverLife';", + " }", + " update(particle, delta) {", + " let speed = particle.velocity.length();", + " let limit = this.speed.genValue(particle.age / particle.life);", + " if (speed > limit) {", + " const percent = (speed - limit) / speed;", + " particle.velocity.multiplyScalar(1 - percent * this.dampen * delta * 20);", + " }", + " }", + " toJSON() {", + " return {", + " type: this.type,", + " speed: this.speed.toJSON(),", + " dampen: this.dampen,", + " };", + " }", + " static fromJSON(json) {", + " return new LimitSpeedOverLife(ValueGeneratorFromJSON(json.speed), json.dampen);", + " }", + " frameUpdate(delta) { }", + " clone() {", + " return new LimitSpeedOverLife(this.speed.clone(), this.dampen);", + " }", + " reset() { }", + "}", + "", + "const BehaviorTypes = {", + " ApplyForce: {", + " type: 'ApplyForce',", + " constructor: ApplyForce,", + " params: [", + " ['direction', 'vec3'],", + " ['magnitude', 'value'],", + " ],", + " loadJSON: ApplyForce.fromJSON,", + " },", + " Noise: {", + " type: 'Noise',", + " constructor: Noise,", + " params: [", + " ['frequency', 'value'],", + " ['power', 'value'],", + " ['positionAmount', 'value'],", + " ['rotationAmount', 'value'],", + " ],", + " loadJSON: Noise.fromJSON,", + " },", + " TurbulenceField: {", + " type: 'TurbulenceField',", + " constructor: TurbulenceField,", + " params: [", + " ['scale', 'vec3'],", + " ['octaves', 'number'],", + " ['velocityMultiplier', 'vec3'],", + " ['timeScale', 'vec3'],", + " ],", + " loadJSON: TurbulenceField.fromJSON,", + " },", + " GravityForce: {", + " type: 'GravityForce',", + " constructor: GravityForce,", + " params: [", + " ['center', 'vec3'],", + " ['magnitude', 'number'],", + " ],", + " loadJSON: GravityForce.fromJSON,", + " },", + " ColorOverLife: {", + " type: 'ColorOverLife',", + " constructor: ColorOverLife,", + " params: [['color', 'colorFunc']],", + " loadJSON: ColorOverLife.fromJSON,", + " },", + " RotationOverLife: {", + " type: 'RotationOverLife',", + " constructor: RotationOverLife,", + " params: [['angularVelocity', 'valueFunc']],", + " loadJSON: RotationOverLife.fromJSON,", + " },", + " Rotation3DOverLife: {", + " type: 'Rotation3DOverLife',", + " constructor: Rotation3DOverLife,", + " params: [['angularVelocity', 'rotationFunc']],", + " loadJSON: Rotation3DOverLife.fromJSON,", + " },", + " SizeOverLife: {", + " type: 'SizeOverLife',", + " constructor: SizeOverLife,", + " params: [['size', 'valueFunc']],", + " loadJSON: SizeOverLife.fromJSON,", + " },", + " ColorBySpeed: {", + " type: 'ColorBySpeed',", + " constructor: ColorBySpeed,", + " params: [", + " ['color', 'colorFunc'],", + " ['speedRange', 'range'],", + " ],", + " loadJSON: ColorBySpeed.fromJSON,", + " },", + " RotationBySpeed: {", + " type: 'RotationBySpeed',", + " constructor: RotationBySpeed,", + " params: [", + " ['angularVelocity', 'valueFunc'],", + " ['speedRange', 'range'],", + " ],", + " loadJSON: RotationBySpeed.fromJSON,", + " },", + " SizeBySpeed: {", + " type: 'SizeBySpeed',", + " constructor: SizeBySpeed,", + " params: [", + " ['size', 'valueFunc'],", + " ['speedRange', 'range'],", + " ],", + " loadJSON: SizeBySpeed.fromJSON,", + " },", + " SpeedOverLife: {", + " type: 'SpeedOverLife',", + " constructor: SpeedOverLife,", + " params: [['speed', 'valueFunc']],", + " loadJSON: SpeedOverLife.fromJSON,", + " },", + " FrameOverLife: {", + " type: 'FrameOverLife',", + " constructor: FrameOverLife,", + " params: [['frame', 'valueFunc']],", + " loadJSON: FrameOverLife.fromJSON,", + " },", + " ForceOverLife: {", + " type: 'ForceOverLife',", + " constructor: ForceOverLife,", + " params: [", + " ['x', 'valueFunc'],", + " ['y', 'valueFunc'],", + " ['z', 'valueFunc'],", + " ],", + " loadJSON: ForceOverLife.fromJSON,", + " },", + " OrbitOverLife: {", + " type: 'OrbitOverLife',", + " constructor: OrbitOverLife,", + " params: [", + " ['orbitSpeed', 'valueFunc'],", + " ['axis', 'vec3'],", + " ],", + " loadJSON: OrbitOverLife.fromJSON,", + " },", + " WidthOverLength: {", + " type: 'WidthOverLength',", + " constructor: WidthOverLength,", + " params: [['width', 'valueFunc']],", + " loadJSON: WidthOverLength.fromJSON,", + " },", + " ChangeEmitDirection: {", + " type: 'ChangeEmitDirection',", + " constructor: ChangeEmitDirection,", + " params: [['angle', 'value']],", + " loadJSON: ChangeEmitDirection.fromJSON,", + " },", + " EmitSubParticleSystem: {", + " type: 'EmitSubParticleSystem',", + " constructor: EmitSubParticleSystem,", + " params: [", + " ['particleSystem', 'self'],", + " ['useVelocityAsBasis', 'boolean'],", + " ['subParticleSystem', 'particleSystem'],", + " ['mode', 'number'],", + " ['emitProbability', 'number'],", + " ],", + " loadJSON: EmitSubParticleSystem.fromJSON,", + " },", + " LimitSpeedOverLife: {", + " type: 'LimitSpeedOverLife',", + " constructor: LimitSpeedOverLife,", + " params: [", + " ['speed', 'valueFunc'],", + " ['dampen', 'number'],", + " ],", + " loadJSON: LimitSpeedOverLife.fromJSON,", + " },", + "};", + "function BehaviorFromJSON(json, particleSystem) {", + " return BehaviorTypes[json.type].loadJSON(json, particleSystem);", + "}", + "", + "let EmitterMode = void 0;", + "(function (EmitterMode) {", + " EmitterMode[EmitterMode[\"Random\"] = 0] = \"Random\";", + " EmitterMode[EmitterMode[\"Loop\"] = 1] = \"Loop\";", + " EmitterMode[EmitterMode[\"PingPong\"] = 2] = \"PingPong\";", + " EmitterMode[EmitterMode[\"Burst\"] = 3] = \"Burst\";", + "})(EmitterMode || (EmitterMode = {}));", + "function getValueFromEmitterMode(mode, currentValue, spread) {", + " let u;", + " if (EmitterMode.Random === mode) {", + " currentValue = Math.random();", + " }", + " if (spread > 0) {", + " u = Math.floor(currentValue / spread) * spread;", + " }", + " else {", + " u = currentValue;", + " }", + " switch (mode) {", + " case EmitterMode.Loop:", + " u = u % 1;", + " break;", + " case EmitterMode.PingPong:", + " u = Math.abs((u % 2) - 1);", + " break;", + " }", + " return u;", + "}", + "", + "class ConeEmitter {", + " constructor(parameters = {}) {", + " var _a, _b, _c, _d, _e, _f, _g;", + " this.type = 'cone';", + " this.currentValue = 0;", + " this.radius = (_a = parameters.radius) !== null && _a !== void 0 ? _a : 10;", + " this.arc = (_b = parameters.arc) !== null && _b !== void 0 ? _b : 2.0 * Math.PI;", + " this.thickness = (_c = parameters.thickness) !== null && _c !== void 0 ? _c : 1;", + " this.angle = (_d = parameters.angle) !== null && _d !== void 0 ? _d : Math.PI / 6;", + " this.mode = (_e = parameters.mode) !== null && _e !== void 0 ? _e : EmitterMode.Random;", + " this.spread = (_f = parameters.spread) !== null && _f !== void 0 ? _f : 0;", + " this.speed = (_g = parameters.speed) !== null && _g !== void 0 ? _g : new ConstantValue(1);", + " }", + " update(system, delta) {", + " if (EmitterMode.Random != this.mode) {", + " this.currentValue += this.speed.genValue(system.emissionState.time / system.duration) * delta;", + " }", + " }", + " initialize(p) {", + " const u = getValueFromEmitterMode(this.mode, this.currentValue, this.spread);", + " const rand = THREE.MathUtils.lerp(1 - this.thickness, 1, Math.random());", + " const theta = u * this.arc;", + " const r = Math.sqrt(rand);", + " const sinTheta = Math.sin(theta);", + " const cosTheta = Math.cos(theta);", + " p.position.x = r * cosTheta;", + " p.position.y = r * sinTheta;", + " p.position.z = 0;", + " const angle = this.angle * r;", + " p.velocity.set(0, 0, Math.cos(angle)).addScaledVector(p.position, Math.sin(angle)).multiplyScalar(p.startSpeed);", + " p.position.multiplyScalar(this.radius);", + " }", + " toJSON() {", + " return {", + " type: 'cone',", + " radius: this.radius,", + " arc: this.arc,", + " thickness: this.thickness,", + " angle: this.angle,", + " mode: this.mode,", + " spread: this.spread,", + " speed: this.speed.toJSON(),", + " };", + " }", + " static fromJSON(json) {", + " return new ConeEmitter({", + " radius: json.radius,", + " arc: json.arc,", + " thickness: json.thickness,", + " angle: json.angle,", + " mode: json.mode,", + " speed: json.speed ? ValueGeneratorFromJSON(json.speed) : undefined,", + " spread: json.spread,", + " });", + " }", + " clone() {", + " return new ConeEmitter({", + " radius: this.radius,", + " arc: this.arc,", + " thickness: this.thickness,", + " angle: this.angle,", + " mode: this.mode,", + " speed: this.speed.clone(),", + " spread: this.spread,", + " });", + " }", + "}", + "", + "class CircleEmitter {", + " constructor(parameters = {}) {", + " var _a, _b, _c, _d, _e, _f;", + " this.type = 'circle';", + " this.currentValue = 0;", + " this.radius = (_a = parameters.radius) !== null && _a !== void 0 ? _a : 10;", + " this.arc = (_b = parameters.arc) !== null && _b !== void 0 ? _b : 2.0 * Math.PI;", + " this.thickness = (_c = parameters.thickness) !== null && _c !== void 0 ? _c : 1;", + " this.mode = (_d = parameters.mode) !== null && _d !== void 0 ? _d : EmitterMode.Random;", + " this.spread = (_e = parameters.spread) !== null && _e !== void 0 ? _e : 0;", + " this.speed = (_f = parameters.speed) !== null && _f !== void 0 ? _f : new ConstantValue(1);", + " }", + " update(system, delta) {", + " this.currentValue += this.speed.genValue(system.emissionState.time / system.duration) * delta;", + " }", + " initialize(p) {", + " const u = getValueFromEmitterMode(this.mode, this.currentValue, this.spread);", + " const r = THREE.MathUtils.lerp(1 - this.thickness, 1, Math.random());", + " const theta = u * this.arc;", + " p.position.x = Math.cos(theta);", + " p.position.y = Math.sin(theta);", + " p.position.z = 0;", + " p.velocity.copy(p.position).multiplyScalar(p.startSpeed);", + " p.position.multiplyScalar(this.radius * r);", + " }", + " toJSON() {", + " return {", + " type: 'circle',", + " radius: this.radius,", + " arc: this.arc,", + " thickness: this.thickness,", + " mode: this.mode,", + " spread: this.spread,", + " speed: this.speed.toJSON(),", + " };", + " }", + " static fromJSON(json) {", + " return new CircleEmitter({", + " radius: json.radius,", + " arc: json.arc,", + " thickness: json.thickness,", + " mode: json.mode,", + " speed: json.speed ? ValueGeneratorFromJSON(json.speed) : undefined,", + " spread: json.spread,", + " });", + " }", + " clone() {", + " return new CircleEmitter({", + " radius: this.radius,", + " arc: this.arc,", + " thickness: this.thickness,", + " mode: this.mode,", + " speed: this.speed.clone(),", + " spread: this.spread,", + " });", + " }", + "}", + "", + "class DonutEmitter {", + " constructor(parameters = {}) {", + " var _a, _b, _c, _d, _e, _f, _g;", + " this.type = 'donut';", + " this.currentValue = 0;", + " this.radius = (_a = parameters.radius) !== null && _a !== void 0 ? _a : 10;", + " this.arc = (_b = parameters.arc) !== null && _b !== void 0 ? _b : 2.0 * Math.PI;", + " this.thickness = (_c = parameters.thickness) !== null && _c !== void 0 ? _c : 1;", + " this.donutRadius = (_d = parameters.donutRadius) !== null && _d !== void 0 ? _d : this.radius * 0.2;", + " this.mode = (_e = parameters.mode) !== null && _e !== void 0 ? _e : EmitterMode.Random;", + " this.spread = (_f = parameters.spread) !== null && _f !== void 0 ? _f : 0;", + " this.speed = (_g = parameters.speed) !== null && _g !== void 0 ? _g : new ConstantValue(1);", + " }", + " update(system, delta) {", + " if (EmitterMode.Random != this.mode) {", + " this.currentValue += this.speed.genValue(system.emissionState.time / system.duration) * delta;", + " }", + " }", + " initialize(p) {", + " const u = getValueFromEmitterMode(this.mode, this.currentValue, this.spread);", + " const v = Math.random();", + " const rand = THREE.MathUtils.lerp(1 - this.thickness, 1, Math.random());", + " const theta = u * this.arc;", + " const phi = v * Math.PI * 2;", + " const sinTheta = Math.sin(theta);", + " const cosTheta = Math.cos(theta);", + " p.position.x = this.radius * cosTheta;", + " p.position.y = this.radius * sinTheta;", + " p.position.z = 0;", + " p.velocity.z = this.donutRadius * rand * Math.sin(phi);", + " p.velocity.x = this.donutRadius * rand * Math.cos(phi) * cosTheta;", + " p.velocity.y = this.donutRadius * rand * Math.cos(phi) * sinTheta;", + " p.position.add(p.velocity);", + " p.velocity.normalize().multiplyScalar(p.startSpeed);", + " }", + " toJSON() {", + " return {", + " type: 'donut',", + " radius: this.radius,", + " arc: this.arc,", + " thickness: this.thickness,", + " donutRadius: this.donutRadius,", + " mode: this.mode,", + " spread: this.spread,", + " speed: this.speed.toJSON(),", + " };", + " }", + " static fromJSON(json) {", + " return new DonutEmitter({", + " radius: json.radius,", + " arc: json.arc,", + " thickness: json.thickness,", + " donutRadius: json.donutRadius,", + " mode: json.mode,", + " speed: json.speed ? ValueGeneratorFromJSON(json.speed) : undefined,", + " spread: json.spread,", + " });", + " }", + " clone() {", + " return new DonutEmitter({", + " radius: this.radius,", + " arc: this.arc,", + " thickness: this.thickness,", + " donutRadius: this.donutRadius,", + " mode: this.mode,", + " speed: this.speed.clone(),", + " spread: this.spread,", + " });", + " }", + "}", + "", + "class PointEmitter {", + " constructor() {", + " this.type = 'point';", + " }", + " update(system, delta) { }", + " initialize(p) {", + " const u = Math.random();", + " const v = Math.random();", + " const theta = u * Math.PI * 2;", + " const phi = Math.acos(2.0 * v - 1.0);", + " const r = Math.cbrt(Math.random());", + " const sinTheta = Math.sin(theta);", + " const cosTheta = Math.cos(theta);", + " const sinPhi = Math.sin(phi);", + " const cosPhi = Math.cos(phi);", + " p.velocity.x = r * sinPhi * cosTheta;", + " p.velocity.y = r * sinPhi * sinTheta;", + " p.velocity.z = r * cosPhi;", + " p.velocity.multiplyScalar(p.startSpeed);", + " p.position.setScalar(0);", + " }", + " toJSON() {", + " return {", + " type: 'point',", + " };", + " }", + " static fromJSON(json) {", + " return new PointEmitter();", + " }", + " clone() {", + " return new PointEmitter();", + " }", + "}", + "", + "class SphereEmitter {", + " constructor(parameters = {}) {", + " var _a, _b, _c, _d, _e, _f;", + " this.type = 'sphere';", + " this.currentValue = 0;", + " this.radius = (_a = parameters.radius) !== null && _a !== void 0 ? _a : 10;", + " this.arc = (_b = parameters.arc) !== null && _b !== void 0 ? _b : 2.0 * Math.PI;", + " this.thickness = (_c = parameters.thickness) !== null && _c !== void 0 ? _c : 1;", + " this.mode = (_d = parameters.mode) !== null && _d !== void 0 ? _d : EmitterMode.Random;", + " this.spread = (_e = parameters.spread) !== null && _e !== void 0 ? _e : 0;", + " this.speed = (_f = parameters.speed) !== null && _f !== void 0 ? _f : new ConstantValue(1);", + " }", + " update(system, delta) {", + " if (EmitterMode.Random != this.mode) {", + " this.currentValue += this.speed.genValue(system.emissionState.time / system.duration) * delta;", + " }", + " }", + " initialize(p) {", + " const u = getValueFromEmitterMode(this.mode, this.currentValue, this.spread);", + " const v = Math.random();", + " const rand = THREE.MathUtils.lerp(1 - this.thickness, 1, Math.random());", + " const theta = u * this.arc;", + " const phi = Math.acos(2.0 * v - 1.0);", + " const sinTheta = Math.sin(theta);", + " const cosTheta = Math.cos(theta);", + " const sinPhi = Math.sin(phi);", + " const cosPhi = Math.cos(phi);", + " p.position.x = sinPhi * cosTheta;", + " p.position.y = sinPhi * sinTheta;", + " p.position.z = cosPhi;", + " p.velocity.copy(p.position).multiplyScalar(p.startSpeed);", + " p.position.multiplyScalar(this.radius * rand);", + " }", + " toJSON() {", + " return {", + " type: 'sphere',", + " radius: this.radius,", + " arc: this.arc,", + " thickness: this.thickness,", + " mode: this.mode,", + " spread: this.spread,", + " speed: this.speed.toJSON(),", + " };", + " }", + " static fromJSON(json) {", + " return new SphereEmitter({", + " radius: json.radius,", + " arc: json.arc,", + " thickness: json.thickness,", + " mode: json.mode,", + " speed: json.speed ? ValueGeneratorFromJSON(json.speed) : undefined,", + " spread: json.spread,", + " });", + " }", + " clone() {", + " return new SphereEmitter({", + " radius: this.radius,", + " arc: this.arc,", + " thickness: this.thickness,", + " mode: this.mode,", + " speed: this.speed.clone(),", + " spread: this.spread,", + " });", + " }", + "}", + "", + "class HemisphereEmitter {", + " constructor(parameters = {}) {", + " var _a, _b, _c, _d, _e, _f;", + " this.type = 'sphere';", + " this.currentValue = 0;", + " this.radius = (_a = parameters.radius) !== null && _a !== void 0 ? _a : 10;", + " this.arc = (_b = parameters.arc) !== null && _b !== void 0 ? _b : 2.0 * Math.PI;", + " this.thickness = (_c = parameters.thickness) !== null && _c !== void 0 ? _c : 1;", + " this.mode = (_d = parameters.mode) !== null && _d !== void 0 ? _d : EmitterMode.Random;", + " this.spread = (_e = parameters.spread) !== null && _e !== void 0 ? _e : 0;", + " this.speed = (_f = parameters.speed) !== null && _f !== void 0 ? _f : new ConstantValue(1);", + " }", + " update(system, delta) {", + " if (EmitterMode.Random != this.mode) {", + " this.currentValue += this.speed.genValue(system.emissionState.time / system.duration) * delta;", + " }", + " }", + " initialize(p) {", + " const u = getValueFromEmitterMode(this.mode, this.currentValue, this.spread);", + " const v = Math.random();", + " const rand = THREE.MathUtils.lerp(1 - this.thickness, 1, Math.random());", + " const theta = u * this.arc;", + " const phi = Math.acos(v);", + " const sinTheta = Math.sin(theta);", + " const cosTheta = Math.cos(theta);", + " const sinPhi = Math.sin(phi);", + " const cosPhi = Math.cos(phi);", + " p.position.x = sinPhi * cosTheta;", + " p.position.y = sinPhi * sinTheta;", + " p.position.z = cosPhi;", + " p.velocity.copy(p.position).multiplyScalar(p.startSpeed);", + " p.position.multiplyScalar(this.radius * rand);", + " }", + " toJSON() {", + " return {", + " type: 'hemisphere',", + " radius: this.radius,", + " arc: this.arc,", + " thickness: this.thickness,", + " mode: this.mode,", + " spread: this.spread,", + " speed: this.speed.toJSON(),", + " };", + " }", + " static fromJSON(json) {", + " return new HemisphereEmitter({", + " radius: json.radius,", + " arc: json.arc,", + " thickness: json.thickness,", + " mode: json.mode,", + " speed: json.speed ? ValueGeneratorFromJSON(json.speed) : undefined,", + " spread: json.spread,", + " });", + " }", + " clone() {", + " return new HemisphereEmitter({", + " radius: this.radius,", + " arc: this.arc,", + " thickness: this.thickness,", + " mode: this.mode,", + " speed: this.speed.clone(),", + " spread: this.spread,", + " });", + " }", + "}", + "", + "class GridEmitter {", + " constructor(parameters = {}) {", + " var _a, _b, _c, _d;", + " this.type = 'grid';", + " this.width = (_a = parameters.width) !== null && _a !== void 0 ? _a : 1;", + " this.height = (_b = parameters.height) !== null && _b !== void 0 ? _b : 1;", + " this.column = (_c = parameters.column) !== null && _c !== void 0 ? _c : 10;", + " this.row = (_d = parameters.row) !== null && _d !== void 0 ? _d : 10;", + " }", + " initialize(p) {", + " const r = Math.floor(Math.random() * this.row);", + " const c = Math.floor(Math.random() * this.column);", + " p.position.x = (c * this.width) / this.column - this.width / 2;", + " p.position.y = (r * this.height) / this.row - this.height / 2;", + " p.position.z = 0;", + " p.velocity.set(0, 0, p.startSpeed);", + " }", + " toJSON() {", + " return {", + " type: 'grid',", + " width: this.width,", + " height: this.height,", + " column: this.column,", + " row: this.row,", + " };", + " }", + " static fromJSON(json) {", + " return new GridEmitter(json);", + " }", + " clone() {", + " return new GridEmitter({", + " width: this.width,", + " height: this.height,", + " column: this.column,", + " row: this.row,", + " });", + " }", + " update(system, delta) { }", + "}", + "", + "class MeshSurfaceEmitter {", + " get geometry() {", + " return this._geometry;", + " }", + " set geometry(geometry) {", + " this._geometry = geometry;", + " if (geometry === undefined) {", + " return;", + " }", + " if (typeof geometry === 'string') {", + " return;", + " }", + " const tri = new THREE.Triangle();", + " this._triangleIndexToArea.length = 0;", + " let area = 0;", + " if (!geometry.getIndex()) {", + " return;", + " }", + " const array = geometry.getIndex().array;", + " const triCount = array.length / 3;", + " this._triangleIndexToArea.push(0);", + " for (let i = 0; i < triCount; i++) {", + " tri.setFromAttributeAndIndices(geometry.getAttribute('position'), array[i * 3], array[i * 3 + 1], array[i * 3 + 2]);", + " area += tri.getArea();", + " this._triangleIndexToArea.push(area);", + " }", + " geometry.userData.triangleIndexToArea = this._triangleIndexToArea;", + " }", + " constructor(geometry) {", + " this.type = 'mesh_surface';", + " this._triangleIndexToArea = [];", + " this._tempA = new THREE.Vector3();", + " this._tempB = new THREE.Vector3();", + " this._tempC = new THREE.Vector3();", + " if (!geometry) {", + " return;", + " }", + " this.geometry = geometry;", + " }", + " initialize(p) {", + " const geometry = this._geometry;", + " if (!geometry || geometry.getIndex() === null) {", + " p.position.set(0, 0, 0);", + " p.velocity.set(0, 0, 1).multiplyScalar(p.startSpeed);", + " return;", + " }", + " const triCount = this._triangleIndexToArea.length - 1;", + " let left = 0, right = triCount;", + " const target = Math.random() * this._triangleIndexToArea[triCount];", + " while (left + 1 < right) {", + " const mid = Math.floor((left + right) / 2);", + " if (target < this._triangleIndexToArea[mid]) {", + " right = mid;", + " }", + " else {", + " left = mid;", + " }", + " }", + " let u1 = Math.random();", + " let u2 = Math.random();", + " if (u1 + u2 > 1) {", + " u1 = 1 - u1;", + " u2 = 1 - u2;", + " }", + " const index1 = geometry.getIndex().array[left * 3];", + " const index2 = geometry.getIndex().array[left * 3 + 1];", + " const index3 = geometry.getIndex().array[left * 3 + 2];", + " const positionBuffer = geometry.getAttribute('position');", + " this._tempA.fromBufferAttribute(positionBuffer, index1);", + " this._tempB.fromBufferAttribute(positionBuffer, index2);", + " this._tempC.fromBufferAttribute(positionBuffer, index3);", + " this._tempB.sub(this._tempA);", + " this._tempC.sub(this._tempA);", + " this._tempA.addScaledVector(this._tempB, u1).addScaledVector(this._tempC, u2);", + " p.position.copy(this._tempA);", + " this._tempA.copy(this._tempB).cross(this._tempC).normalize();", + " p.velocity.copy(this._tempA).normalize().multiplyScalar(p.startSpeed);", + " }", + " toJSON() {", + " return {", + " type: 'mesh_surface',", + " mesh: this._geometry ? this._geometry.uuid : '',", + " };", + " }", + " static fromJSON(json, meta) {", + " return new MeshSurfaceEmitter(meta.geometries[json.geometry]);", + " }", + " clone() {", + " return new MeshSurfaceEmitter(this._geometry);", + " }", + " update(system, delta) { }", + "}", + "", + "const EmitterShapes = {", + " circle: {", + " type: 'circle',", + " params: [", + " ['radius', 'number'],", + " ['arc', 'radian'],", + " ['thickness', 'number'],", + " ['mode', 'emitterMode'],", + " ['spread', 'number'],", + " ['speed', 'valueFunc'],", + " ],", + " constructor: CircleEmitter,", + " loadJSON: CircleEmitter.fromJSON,", + " },", + " cone: {", + " type: 'cone',", + " params: [", + " ['radius', 'number'],", + " ['arc', 'radian'],", + " ['thickness', 'number'],", + " ['angle', 'radian'],", + " ['mode', 'emitterMode'],", + " ['spread', 'number'],", + " ['speed', 'valueFunc'],", + " ],", + " constructor: ConeEmitter,", + " loadJSON: ConeEmitter.fromJSON,", + " },", + " donut: {", + " type: 'donut',", + " params: [", + " ['radius', 'number'],", + " ['arc', 'radian'],", + " ['thickness', 'number'],", + " ['donutRadius', 'number'],", + " ['mode', 'emitterMode'],", + " ['spread', 'number'],", + " ['speed', 'valueFunc'],", + " ],", + " constructor: DonutEmitter,", + " loadJSON: DonutEmitter.fromJSON,", + " },", + " point: { type: 'point', params: [], constructor: PointEmitter, loadJSON: PointEmitter.fromJSON },", + " sphere: {", + " type: 'sphere',", + " params: [", + " ['radius', 'number'],", + " ['arc', 'radian'],", + " ['thickness', 'number'],", + " ['angle', 'radian'],", + " ['mode', 'emitterMode'],", + " ['spread', 'number'],", + " ['speed', 'valueFunc'],", + " ],", + " constructor: SphereEmitter,", + " loadJSON: SphereEmitter.fromJSON,", + " },", + " hemisphere: {", + " type: 'hemisphere',", + " params: [", + " ['radius', 'number'],", + " ['arc', 'radian'],", + " ['thickness', 'number'],", + " ['angle', 'radian'],", + " ['mode', 'emitterMode'],", + " ['spread', 'number'],", + " ['speed', 'valueFunc'],", + " ],", + " constructor: HemisphereEmitter,", + " loadJSON: HemisphereEmitter.fromJSON,", + " },", + " grid: {", + " type: 'grid',", + " params: [", + " ['width', 'number'],", + " ['height', 'number'],", + " ['rows', 'number'],", + " ['column', 'number'],", + " ],", + " constructor: GridEmitter,", + " loadJSON: GridEmitter.fromJSON,", + " },", + " mesh_surface: {", + " type: 'mesh_surface',", + " params: [['geometry', 'geometry']],", + " constructor: MeshSurfaceEmitter,", + " loadJSON: MeshSurfaceEmitter.fromJSON,", + " },", + "};", + "function EmitterFromJSON(json, meta) {", + " return EmitterShapes[json.type].loadJSON(json, meta);", + "}", + "", + "let RenderMode = void 0;", + "(function (RenderMode) {", + " RenderMode[RenderMode[\"BillBoard\"] = 0] = \"BillBoard\";", + " RenderMode[RenderMode[\"StretchedBillBoard\"] = 1] = \"StretchedBillBoard\";", + " RenderMode[RenderMode[\"Mesh\"] = 2] = \"Mesh\";", + " RenderMode[RenderMode[\"Trail\"] = 3] = \"Trail\";", + " RenderMode[RenderMode[\"HorizontalBillBoard\"] = 4] = \"HorizontalBillBoard\";", + " RenderMode[RenderMode[\"VerticalBillBoard\"] = 5] = \"VerticalBillBoard\";", + "})(RenderMode || (RenderMode = {}));", + "class VFXBatch extends THREE.Mesh {", + " constructor(settings) {", + " super();", + " this.type = 'VFXBatch';", + " this.maxParticles = 1000;", + " this.systems = new Set();", + " const layers = new THREE.Layers();", + " layers.mask = settings.layers.mask;", + " const newMat = settings.material.clone();", + " newMat.defines = {};", + " Object.assign(newMat.defines, settings.material.defines);", + " this.settings = {", + " instancingGeometry: settings.instancingGeometry,", + " renderMode: settings.renderMode,", + " renderOrder: settings.renderOrder,", + " material: newMat,", + " uTileCount: settings.uTileCount,", + " vTileCount: settings.vTileCount,", + " layers: layers,", + " };", + " this.frustumCulled = false;", + " this.renderOrder = this.settings.renderOrder;", + " }", + " addSystem(system) {", + " this.systems.add(system);", + " }", + " removeSystem(system) {", + " this.systems.delete(system);", + " }", + "}", + "", + "const UP = new THREE.Vector3(0, 0, 1);", + "const tempQ$1 = new THREE.Quaternion();", + "const tempV$1 = new THREE.Vector3();", + "const tempV2$1 = new THREE.Vector3();", + "new THREE.Vector3();", + "const PREWARM_FPS$1 = 60;", + "const DEFAULT_GEOMETRY$1 = new THREE.PlaneGeometry(1, 1, 1, 1);", + "class ParticleSystem {", + " set time(time) {", + " this.emissionState.time = time;", + " }", + " get time() {", + " return this.emissionState.time;", + " }", + " get layers() {", + " return this.rendererSettings.layers;", + " }", + " get texture() {", + " return this.rendererSettings.material.map;", + " }", + " set texture(texture) {", + " this.rendererSettings.material.map = texture;", + " this.neededToUpdateRender = true;", + " }", + " get material() {", + " return this.rendererSettings.material;", + " }", + " set material(material) {", + " this.rendererSettings.material = material;", + " this.neededToUpdateRender = true;", + " }", + " get uTileCount() {", + " return this.rendererSettings.uTileCount;", + " }", + " set uTileCount(u) {", + " this.rendererSettings.uTileCount = u;", + " this.neededToUpdateRender = true;", + " }", + " get vTileCount() {", + " return this.rendererSettings.vTileCount;", + " }", + " set vTileCount(v) {", + " this.rendererSettings.vTileCount = v;", + " this.neededToUpdateRender = true;", + " }", + " get instancingGeometry() {", + " return this.rendererSettings.instancingGeometry;", + " }", + " set instancingGeometry(geometry) {", + " this.restart();", + " this.particles.length = 0;", + " this.rendererSettings.instancingGeometry = geometry;", + " this.neededToUpdateRender = true;", + " }", + " get renderMode() {", + " return this.rendererSettings.renderMode;", + " }", + " set renderMode(renderMode) {", + " if ((this.rendererSettings.renderMode != RenderMode.Trail && renderMode === RenderMode.Trail) ||", + " (this.rendererSettings.renderMode == RenderMode.Trail && renderMode !== RenderMode.Trail)) {", + " this.restart();", + " this.particles.length = 0;", + " }", + " if (this.rendererSettings.renderMode !== renderMode) {", + " switch (renderMode) {", + " case RenderMode.Trail:", + " this.rendererEmitterSettings = {", + " startLength: new ConstantValue(30),", + " followLocalOrigin: false,", + " };", + " break;", + " case RenderMode.Mesh:", + " this.rendererEmitterSettings = {", + " geometry: new THREE.PlaneGeometry(1, 1),", + " };", + " this.startRotation = new AxisAngleGenerator(new THREE.Vector3(0, 1, 0), new ConstantValue(0));", + " break;", + " case RenderMode.StretchedBillBoard:", + " this.rendererEmitterSettings = { speedFactor: 0, lengthFactor: 2 };", + " if (this.rendererSettings.renderMode === RenderMode.Mesh) {", + " this.startRotation = new ConstantValue(0);", + " }", + " break;", + " case RenderMode.BillBoard:", + " case RenderMode.VerticalBillBoard:", + " case RenderMode.HorizontalBillBoard:", + " this.rendererEmitterSettings = {};", + " if (this.rendererSettings.renderMode === RenderMode.Mesh) {", + " this.startRotation = new ConstantValue(0);", + " }", + " break;", + " }", + " }", + " this.rendererSettings.renderMode = renderMode;", + " this.neededToUpdateRender = true;", + " }", + " get renderOrder() {", + " return this.rendererSettings.renderOrder;", + " }", + " set renderOrder(renderOrder) {", + " this.rendererSettings.renderOrder = renderOrder;", + " this.neededToUpdateRender = true;", + " }", + " get blending() {", + " return this.rendererSettings.material.blending;", + " }", + " set blending(blending) {", + " this.rendererSettings.material.blending = blending;", + " this.neededToUpdateRender = true;", + " }", + " constructor(parameters) {", + " var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m, _o, _p, _q, _r, _s, _t, _u, _v, _w, _x;", + " this.temp = new THREE.Vector3();", + " this.travelDistance = 0;", + " this.normalMatrix = new THREE.Matrix3();", + " this.firstTimeUpdate = true;", + " this.autoDestroy = parameters.autoDestroy === undefined ? false : parameters.autoDestroy;", + " this.duration = (_a = parameters.duration) !== null && _a !== void 0 ? _a : 1;", + " this.looping = parameters.looping === undefined ? true : parameters.looping;", + " this.prewarm = parameters.prewarm === undefined ? false : parameters.prewarm;", + " this.startLife = (_b = parameters.startLife) !== null && _b !== void 0 ? _b : new ConstantValue(5);", + " this.startSpeed = (_c = parameters.startSpeed) !== null && _c !== void 0 ? _c : new ConstantValue(0);", + " this.startRotation = (_d = parameters.startRotation) !== null && _d !== void 0 ? _d : new ConstantValue(0);", + " this.startSize = (_e = parameters.startSize) !== null && _e !== void 0 ? _e : new ConstantValue(1);", + " this.startColor = (_f = parameters.startColor) !== null && _f !== void 0 ? _f : new ConstantColor(new THREE.Vector4(1, 1, 1, 1));", + " this.emissionOverTime = (_g = parameters.emissionOverTime) !== null && _g !== void 0 ? _g : new ConstantValue(10);", + " this.emissionOverDistance = (_h = parameters.emissionOverDistance) !== null && _h !== void 0 ? _h : new ConstantValue(0);", + " this.emissionBursts = (_j = parameters.emissionBursts) !== null && _j !== void 0 ? _j : [];", + " this.onlyUsedByOther = (_k = parameters.onlyUsedByOther) !== null && _k !== void 0 ? _k : false;", + " this.emitterShape = (_l = parameters.shape) !== null && _l !== void 0 ? _l : new SphereEmitter();", + " this.behaviors = (_m = parameters.behaviors) !== null && _m !== void 0 ? _m : new Array();", + " this.worldSpace = (_o = parameters.worldSpace) !== null && _o !== void 0 ? _o : false;", + " this.rendererEmitterSettings = (_p = parameters.rendererEmitterSettings) !== null && _p !== void 0 ? _p : {};", + " if (parameters.renderMode === RenderMode.StretchedBillBoard) {", + " const stretchedBillboardSettings = this.rendererEmitterSettings;", + " if (parameters.speedFactor !== undefined) {", + " stretchedBillboardSettings.speedFactor = parameters.speedFactor;", + " }", + " stretchedBillboardSettings.speedFactor = (_q = stretchedBillboardSettings.speedFactor) !== null && _q !== void 0 ? _q : 0;", + " stretchedBillboardSettings.lengthFactor = (_r = stretchedBillboardSettings.lengthFactor) !== null && _r !== void 0 ? _r : 0;", + " }", + " this.rendererSettings = {", + " instancingGeometry: (_s = parameters.instancingGeometry) !== null && _s !== void 0 ? _s : DEFAULT_GEOMETRY$1,", + " renderMode: (_t = parameters.renderMode) !== null && _t !== void 0 ? _t : RenderMode.BillBoard,", + " renderOrder: (_u = parameters.renderOrder) !== null && _u !== void 0 ? _u : 0,", + " material: parameters.material,", + " uTileCount: (_v = parameters.uTileCount) !== null && _v !== void 0 ? _v : 1,", + " vTileCount: (_w = parameters.vTileCount) !== null && _w !== void 0 ? _w : 1,", + " layers: (_x = parameters.layers) !== null && _x !== void 0 ? _x : new THREE.Layers(),", + " };", + " this.neededToUpdateRender = true;", + " this.particles = new Array();", + " this.startTileIndex = parameters.startTileIndex || new ConstantValue(0);", + " this.emitter = new ParticleEmitter(this);", + " this.paused = false;", + " this.particleNum = 0;", + " this.emissionState = {", + " burstIndex: 0,", + " burstWaveIndex: 0,", + " time: 0,", + " waitEmiting: 0,", + " travelDistance: 0,", + " };", + " this.emitEnded = false;", + " this.markForDestroy = false;", + " this.prewarmed = false;", + " }", + " pause() {", + " this.paused = true;", + " }", + " play() {", + " this.paused = false;", + " }", + " spawn(count, emissionState, matrix) {", + " tempQ$1.setFromRotationMatrix(matrix);", + " const translation = tempV$1;", + " const quaternion = tempQ$1;", + " const scale = tempV2$1;", + " matrix.decompose(translation, quaternion, scale);", + " for (let i = 0; i < count; i++) {", + " this.particleNum++;", + " while (this.particles.length < this.particleNum) {", + " if (this.rendererSettings.renderMode === RenderMode.Trail) {", + " this.particles.push(new TrailParticle());", + " }", + " else {", + " this.particles.push(new SpriteParticle());", + " }", + " }", + " const particle = this.particles[this.particleNum - 1];", + " particle.speedModifier = 1;", + " this.startColor.genColor(particle.startColor, this.emissionState.time, {});", + " particle.color.copy(particle.startColor);", + " particle.startSpeed = this.startSpeed.genValue(emissionState.time / this.duration);", + " particle.life = this.startLife.genValue(emissionState.time / this.duration);", + " particle.age = 0;", + " particle.startSize = this.startSize.genValue(emissionState.time / this.duration);", + " particle.uvTile = Math.floor(this.startTileIndex.genValue() + 0.001);", + " particle.size = particle.startSize;", + " if (this.rendererSettings.renderMode === RenderMode.Mesh ||", + " this.rendererSettings.renderMode === RenderMode.BillBoard ||", + " this.rendererSettings.renderMode === RenderMode.VerticalBillBoard ||", + " this.rendererSettings.renderMode === RenderMode.HorizontalBillBoard ||", + " this.rendererSettings.renderMode === RenderMode.StretchedBillBoard) {", + " const sprite = particle;", + " if (this.rendererSettings.renderMode === RenderMode.Mesh) {", + " if (!(sprite.rotation instanceof THREE.Quaternion)) {", + " sprite.rotation = new THREE.Quaternion();", + " }", + " if (this.startRotation.type === 'rotation') {", + " this.startRotation.genValue(sprite.rotation, emissionState.time / this.duration);", + " }", + " else {", + " sprite.rotation.setFromAxisAngle(UP, this.startRotation.genValue((emissionState.time / this.duration)));", + " }", + " }", + " else {", + " if (this.startRotation.type === 'rotation') {", + " sprite.rotation = 0;", + " }", + " else {", + " sprite.rotation = this.startRotation.genValue(emissionState.time / this.duration);", + " }", + " }", + " }", + " else if (this.rendererSettings.renderMode === RenderMode.Trail) {", + " const trail = particle;", + " trail.length = this.rendererEmitterSettings.startLength.genValue(emissionState.time / this.duration);", + " }", + " this.emitterShape.initialize(particle);", + " if (this.rendererSettings.renderMode === RenderMode.Trail &&", + " this.rendererEmitterSettings.followLocalOrigin) {", + " const trail = particle;", + " trail.localPosition = new THREE.Vector3().copy(trail.position);", + " }", + " if (this.worldSpace) {", + " particle.position.applyMatrix4(matrix);", + " particle.startSize =", + " (particle.startSize * (Math.abs(scale.x) + Math.abs(scale.y) + Math.abs(scale.z))) / 3;", + " particle.size = particle.startSize;", + " particle.velocity.multiply(scale).applyMatrix3(this.normalMatrix);", + " if (particle.rotation && particle.rotation instanceof THREE.Quaternion) {", + " particle.rotation.multiplyQuaternions(tempQ$1, particle.rotation);", + " }", + " }", + " else {", + " if (this.onlyUsedByOther) {", + " particle.parentMatrix = matrix;", + " }", + " }", + " for (let j = 0; j < this.behaviors.length; j++) {", + " this.behaviors[j].initialize(particle);", + " }", + " }", + " }", + " endEmit() {", + " this.emitEnded = true;", + " if (this.autoDestroy) {", + " this.markForDestroy = true;", + " }", + " }", + " dispose() {", + " if (this._renderer)", + " this._renderer.deleteSystem(this);", + " this.emitter.dispose();", + " if (this.emitter.parent)", + " this.emitter.parent.remove(this.emitter);", + " }", + " restart() {", + " this.paused = false;", + " this.particleNum = 0;", + " this.emissionState.burstIndex = 0;", + " this.emissionState.burstWaveIndex = 0;", + " this.emissionState.time = 0;", + " this.emissionState.waitEmiting = 0;", + " this.behaviors.forEach((behavior) => {", + " behavior.reset();", + " });", + " this.emitEnded = false;", + " this.markForDestroy = false;", + " this.prewarmed = false;", + " }", + " update(delta) {", + " if (this.paused)", + " return;", + " let currentParent = this.emitter;", + " while (currentParent.parent) {", + " currentParent = currentParent.parent;", + " }", + " if (currentParent.type !== 'Scene') {", + " this.dispose();", + " return;", + " }", + " if (this.firstTimeUpdate) {", + " this.firstTimeUpdate = false;", + " this.emitter.updateWorldMatrix(true, false);", + " }", + " if (this.emitEnded && this.particleNum === 0) {", + " if (this.markForDestroy && this.emitter.parent)", + " this.dispose();", + " return;", + " }", + " if (this.looping && this.prewarm && !this.prewarmed) {", + " this.prewarmed = true;", + " for (let i = 0; i < this.duration * PREWARM_FPS$1; i++) {", + " this.update(1.0 / PREWARM_FPS$1);", + " }", + " }", + " if (delta > 0.1) {", + " delta = 0.1;", + " }", + " if (this.neededToUpdateRender) {", + " if (this._renderer)", + " this._renderer.updateSystem(this);", + " this.neededToUpdateRender = false;", + " }", + " if (!this.onlyUsedByOther) {", + " this.emit(delta, this.emissionState, this.emitter.matrixWorld);", + " }", + " this.emitterShape.update(this, delta);", + " for (let j = 0; j < this.behaviors.length; j++) {", + " for (let i = 0; i < this.particleNum; i++) {", + " if (!this.particles[i].died) {", + " this.behaviors[j].update(this.particles[i], delta);", + " }", + " }", + " this.behaviors[j].frameUpdate(delta);", + " }", + " for (let i = 0; i < this.particleNum; i++) {", + " if (this.rendererEmitterSettings.followLocalOrigin &&", + " this.particles[i].localPosition) {", + " this.particles[i].position.copy(this.particles[i].localPosition);", + " if (this.particles[i].parentMatrix) {", + " this.particles[i].position.applyMatrix4(this.particles[i].parentMatrix);", + " }", + " else {", + " this.particles[i].position.applyMatrix4(this.emitter.matrixWorld);", + " }", + " }", + " else {", + " this.particles[i].position.addScaledVector(this.particles[i].velocity, delta * this.particles[i].speedModifier);", + " }", + " this.particles[i].age += delta;", + " }", + " if (this.rendererSettings.renderMode === RenderMode.Trail) {", + " for (let i = 0; i < this.particleNum; i++) {", + " const particle = this.particles[i];", + " particle.update();", + " }", + " }", + " for (let i = 0; i < this.particleNum; i++) {", + " const particle = this.particles[i];", + " if (particle.died && (!(particle instanceof TrailParticle) || particle.previous.length === 0)) {", + " this.particles[i] = this.particles[this.particleNum - 1];", + " this.particles[this.particleNum - 1] = particle;", + " this.particleNum--;", + " i--;", + " }", + " }", + " }", + " emit(delta, emissionState, emitterMatrix) {", + " if (emissionState.time > this.duration) {", + " if (this.looping) {", + " emissionState.time -= this.duration;", + " emissionState.burstIndex = 0;", + " this.behaviors.forEach((behavior) => {", + " behavior.reset();", + " });", + " }", + " else {", + " if (!this.emitEnded && !this.onlyUsedByOther) {", + " this.endEmit();", + " }", + " }", + " }", + " this.normalMatrix.getNormalMatrix(emitterMatrix);", + " const totalSpawn = Math.ceil(emissionState.waitEmiting);", + " this.spawn(totalSpawn, emissionState, emitterMatrix);", + " emissionState.waitEmiting -= totalSpawn;", + " while (emissionState.burstIndex < this.emissionBursts.length &&", + " this.emissionBursts[emissionState.burstIndex].time <= emissionState.time) {", + " if (Math.random() < this.emissionBursts[emissionState.burstIndex].probability) {", + " const count = this.emissionBursts[emissionState.burstIndex].count.genValue(this.time);", + " this.spawn(count, emissionState, emitterMatrix);", + " }", + " emissionState.burstIndex++;", + " }", + " if (!this.emitEnded) {", + " emissionState.waitEmiting += delta * this.emissionOverTime.genValue(emissionState.time / this.duration);", + " if (emissionState.previousWorldPos != undefined) {", + " this.temp.set(emitterMatrix.elements[12], emitterMatrix.elements[13], emitterMatrix.elements[14]);", + " emissionState.travelDistance += emissionState.previousWorldPos.distanceTo(this.temp);", + " const emitPerMeter = this.emissionOverDistance.genValue(emissionState.time / this.duration);", + " if (emissionState.travelDistance * emitPerMeter > 0) {", + " const count = Math.floor(emissionState.travelDistance * emitPerMeter);", + " emissionState.travelDistance -= count / emitPerMeter;", + " emissionState.waitEmiting += count;", + " }", + " }", + " }", + " if (emissionState.previousWorldPos === undefined)", + " emissionState.previousWorldPos = new THREE.Vector3();", + " emissionState.previousWorldPos.set(emitterMatrix.elements[12], emitterMatrix.elements[13], emitterMatrix.elements[14]);", + " emissionState.time += delta;", + " }", + " toJSON(meta, options = {}) {", + " const isRootObject = meta === undefined || typeof meta === 'string';", + " if (isRootObject) {", + " meta = {", + " geometries: {},", + " materials: {},", + " textures: {},", + " images: {},", + " shapes: {},", + " skeletons: {},", + " animations: {},", + " nodes: {},", + " };", + " }", + " meta.materials[this.rendererSettings.material.uuid] = this.rendererSettings.material.toJSON(meta);", + " if (options.useUrlForImage) {", + " if (this.texture.source !== undefined) {", + " const image = this.texture.source;", + " meta.images[image.uuid] = {", + " uuid: image.uuid,", + " url: this.texture.image.url,", + " };", + " }", + " }", + " let rendererSettingsJSON;", + " if (this.renderMode === RenderMode.Trail) {", + " rendererSettingsJSON = {", + " startLength: this.rendererEmitterSettings.startLength.toJSON(),", + " followLocalOrigin: this.rendererEmitterSettings.followLocalOrigin,", + " };", + " }", + " else if (this.renderMode === RenderMode.Mesh) {", + " rendererSettingsJSON = {};", + " }", + " else if (this.renderMode === RenderMode.StretchedBillBoard) {", + " rendererSettingsJSON = {", + " speedFactor: this.rendererEmitterSettings.speedFactor,", + " lengthFactor: this.rendererEmitterSettings.lengthFactor,", + " };", + " }", + " else {", + " rendererSettingsJSON = {};", + " }", + " const geometry = this.rendererSettings.instancingGeometry;", + " if (meta.geometries && !meta.geometries[geometry.uuid]) {", + " meta.geometries[geometry.uuid] = geometry.toJSON();", + " }", + " return {", + " version: '2.0',", + " autoDestroy: this.autoDestroy,", + " looping: this.looping,", + " prewarm: this.prewarm,", + " duration: this.duration,", + " shape: this.emitterShape.toJSON(),", + " startLife: this.startLife.toJSON(),", + " startSpeed: this.startSpeed.toJSON(),", + " startRotation: this.startRotation.toJSON(),", + " startSize: this.startSize.toJSON(),", + " startColor: this.startColor.toJSON(),", + " emissionOverTime: this.emissionOverTime.toJSON(),", + " emissionOverDistance: this.emissionOverDistance.toJSON(),", + " emissionBursts: this.emissionBursts.map((burst) => ({", + " time: burst.time,", + " count: burst.count.toJSON(),", + " probability: burst.probability,", + " interval: burst.interval,", + " cycle: burst.cycle,", + " })),", + " onlyUsedByOther: this.onlyUsedByOther,", + " instancingGeometry: this.rendererSettings.instancingGeometry.uuid,", + " renderOrder: this.renderOrder,", + " renderMode: this.renderMode,", + " rendererEmitterSettings: rendererSettingsJSON,", + " material: this.rendererSettings.material.uuid,", + " layers: this.layers.mask,", + " startTileIndex: this.startTileIndex.toJSON(),", + " uTileCount: this.uTileCount,", + " vTileCount: this.vTileCount,", + " behaviors: this.behaviors.map((behavior) => behavior.toJSON()),", + " worldSpace: this.worldSpace,", + " };", + " }", + " static fromJSON(json, meta, dependencies) {", + " var _a, _b;", + " const shape = EmitterFromJSON(json.shape, meta);", + " let rendererEmitterSettings;", + " if (json.renderMode === RenderMode.Trail) {", + " let trailSettings = json.rendererEmitterSettings;", + " rendererEmitterSettings = {", + " startLength: trailSettings.startLength != undefined", + " ? ValueGeneratorFromJSON(trailSettings.startLength)", + " : new ConstantValue(30),", + " followLocalOrigin: trailSettings.followLocalOrigin,", + " };", + " }", + " else if (json.renderMode === RenderMode.Mesh) {", + " rendererEmitterSettings = {};", + " }", + " else if (json.renderMode === RenderMode.StretchedBillBoard) {", + " rendererEmitterSettings = json.rendererEmitterSettings;", + " if (json.speedFactor != undefined) {", + " rendererEmitterSettings.speedFactor = json.speedFactor;", + " }", + " }", + " else {", + " rendererEmitterSettings = {};", + " }", + " const layers = new THREE.Layers();", + " if (json.layers) {", + " layers.mask = json.layers;", + " }", + " const ps = new ParticleSystem({", + " autoDestroy: json.autoDestroy,", + " looping: json.looping,", + " prewarm: json.prewarm,", + " duration: json.duration,", + " shape: shape,", + " startLife: ValueGeneratorFromJSON(json.startLife),", + " startSpeed: ValueGeneratorFromJSON(json.startSpeed),", + " startRotation: GeneratorFromJSON(json.startRotation),", + " startSize: ValueGeneratorFromJSON(json.startSize),", + " startColor: ColorGeneratorFromJSON(json.startColor),", + " emissionOverTime: ValueGeneratorFromJSON(json.emissionOverTime),", + " emissionOverDistance: ValueGeneratorFromJSON(json.emissionOverDistance),", + " emissionBursts: (_a = json.emissionBursts) === null || _a === void 0 ? void 0 : _a.map((burst) => ({", + " time: burst.time,", + " count: typeof burst.count === 'number'", + " ? new ConstantValue(burst.count)", + " : ValueGeneratorFromJSON(burst.count),", + " probability: burst.probability,", + " interval: burst.interval,", + " cycle: burst.cycle,", + " })),", + " onlyUsedByOther: json.onlyUsedByOther,", + " instancingGeometry: meta.geometries[json.instancingGeometry],", + " renderMode: json.renderMode,", + " rendererEmitterSettings: rendererEmitterSettings,", + " renderOrder: json.renderOrder,", + " layers: layers,", + " material: json.material", + " ? meta.materials[json.material]", + " : json.texture", + " ? new THREE.MeshBasicMaterial({", + " map: meta.textures[json.texture],", + " transparent: (_b = json.transparent) !== null && _b !== void 0 ? _b : true,", + " blending: json.blending,", + " side: THREE.DoubleSide,", + " })", + " : new THREE.MeshBasicMaterial({", + " color: 0xffffff,", + " transparent: true,", + " blending: THREE.AdditiveBlending,", + " side: THREE.DoubleSide,", + " }),", + " startTileIndex: typeof json.startTileIndex === 'number'", + " ? new ConstantValue(json.startTileIndex)", + " : ValueGeneratorFromJSON(json.startTileIndex),", + " uTileCount: json.uTileCount,", + " vTileCount: json.vTileCount,", + " behaviors: [],", + " worldSpace: json.worldSpace,", + " });", + " ps.behaviors = json.behaviors.map((behaviorJson) => {", + " const behavior = BehaviorFromJSON(behaviorJson, ps);", + " if (behavior.type === 'EmitSubParticleSystem') {", + " dependencies[behaviorJson.subParticleSystem] = behavior;", + " }", + " return behavior;", + " });", + " return ps;", + " }", + " addBehavior(behavior) {", + " this.behaviors.push(behavior);", + " }", + " getRendererSettings() {", + " return this.rendererSettings;", + " }", + " clone() {", + " const newEmissionBursts = [];", + " for (const emissionBurst of this.emissionBursts) {", + " const newEmissionBurst = {};", + " Object.assign(newEmissionBurst, emissionBurst);", + " newEmissionBursts.push(newEmissionBurst);", + " }", + " const newBehaviors = [];", + " for (const behavior of this.behaviors) {", + " newBehaviors.push(behavior.clone());", + " }", + " let rendererEmitterSettings;", + " if (this.renderMode === RenderMode.Trail) {", + " rendererEmitterSettings = {", + " startLength: this.rendererEmitterSettings.startLength.clone(),", + " followLocalOrigin: this.rendererEmitterSettings.followLocalOrigin,", + " };", + " }", + " else {", + " rendererEmitterSettings = {};", + " }", + " const layers = new THREE.Layers();", + " layers.mask = this.layers.mask;", + " return new ParticleSystem({", + " autoDestroy: this.autoDestroy,", + " looping: this.looping,", + " duration: this.duration,", + " shape: this.emitterShape.clone(),", + " startLife: this.startLife.clone(),", + " startSpeed: this.startSpeed.clone(),", + " startRotation: this.startRotation.clone(),", + " startSize: this.startSize.clone(),", + " startColor: this.startColor.clone(),", + " emissionOverTime: this.emissionOverTime.clone(),", + " emissionOverDistance: this.emissionOverDistance.clone(),", + " emissionBursts: newEmissionBursts,", + " onlyUsedByOther: this.onlyUsedByOther,", + " instancingGeometry: this.rendererSettings.instancingGeometry,", + " renderMode: this.renderMode,", + " renderOrder: this.renderOrder,", + " rendererEmitterSettings: rendererEmitterSettings,", + " material: this.rendererSettings.material,", + " startTileIndex: this.startTileIndex,", + " uTileCount: this.uTileCount,", + " vTileCount: this.vTileCount,", + " behaviors: newBehaviors,", + " worldSpace: this.worldSpace,", + " layers: layers,", + " });", + " }", + "}", + "", + "var particle_frag = `", + "", + "#include ", + "#include ", + "#include ", + "#include ", + "#include ", + "#include ", + "#include ", + "", + "void main() {", + "", + " #include ", + " ", + " vec3 outgoingLight = vec3( 0.0 );", + " vec4 diffuseColor = vColor;", + " ", + " #include ", + " ", + " #ifdef USE_MAP", + " diffuseColor *= texture2D( map, vMapUv);", + " #endif", + " ", + " #include ", + "", + " outgoingLight = diffuseColor.rgb;", + " ", + " #ifdef USE_COLOR_AS_ALPHA", + " gl_FragColor = vec4( outgoingLight, diffuseColor.r );", + " #else", + " gl_FragColor = vec4( outgoingLight, diffuseColor.a );", + " #endif", + " ", + " ", + " #include ", + "", + "}", + "`;", + "", + "var particle_physics_frag = `", + "#define STANDARD", + "#ifdef PHYSICAL", + "#define IOR", + "#define SPECULAR", + "#endif", + "uniform vec3 diffuse;", + "uniform vec3 emissive;", + "uniform float roughness;", + "uniform float metalness;", + "uniform float opacity;", + "#ifdef IOR", + "uniform float ior;", + "#endif", + "#ifdef SPECULAR", + "uniform float specularIntensity;", + "uniform vec3 specularColor;", + "#ifdef USE_SPECULARINTENSITYMAP", + "uniform sampler2D specularIntensityMap;", + "#endif", + "#ifdef USE_SPECULARCOLORMAP", + "uniform sampler2D specularColorMap;", + "#endif", + "#endif", + "#ifdef USE_CLEARCOAT", + "uniform float clearcoat;", + "uniform float clearcoatRoughness;", + "#endif", + "#ifdef USE_IRIDESCENCE", + "uniform float iridescence;", + "uniform float iridescenceIOR;", + "uniform float iridescenceThicknessMinimum;", + "uniform float iridescenceThicknessMaximum;", + "#endif", + "#ifdef USE_SHEEN", + "uniform vec3 sheenColor;", + "uniform float sheenRoughness;", + "#ifdef USE_SHEENCOLORMAP", + "uniform sampler2D sheenColorMap;", + "#endif", + "#ifdef USE_SHEENROUGHNESSMAP", + "uniform sampler2D sheenRoughnessMap;", + "#endif", + "#endif", + "", + "varying vec3 vViewPosition;", + "#include ", + "#include ", + "#include ", + "#include ", + "#include ", + "#include ", + "#include ", + "#include ", + "#include ", + "#include ", + "#include ", + "#include ", + "#include ", + "#include ", + "#include ", + "#include ", + "#include ", + "#include ", + "#include ", + "#include ", + "#include ", + "#include ", + "#include ", + "#include ", + "#include ", + "#include ", + "#include ", + "#include ", + "#include ", + "#include ", + "", + "void main() {", + " #include ", + " vec4 diffuseColor = vec4( diffuse, opacity );", + " ReflectedLight reflectedLight = ReflectedLight( vec3( 0.0 ), vec3( 0.0 ), vec3( 0.0 ), vec3( 0.0 ) );", + " vec3 totalEmissiveRadiance = emissive;", + " #include ", + " #include ", + " #include ", + " #include ", + " #include ", + " #include ", + " #include ", + " #include ", + " #include ", + " #include ", + " #include ", + " #include ", + " // accumulation", + " #include ", + " #include ", + " #include ", + " #include ", + " // modulation", + " #include ", + " vec3 totalDiffuse = reflectedLight.directDiffuse + reflectedLight.indirectDiffuse;", + " vec3 totalSpecular = reflectedLight.directSpecular + reflectedLight.indirectSpecular;", + " #include ", + " vec3 outgoingLight = totalDiffuse + totalSpecular + totalEmissiveRadiance;", + " #ifdef USE_SHEEN", + " // Sheen energy compensation approximation calculation can be found at the end of", + " // https://drive.google.com/file/d/1T0D1VSyR4AllqIJTQAraEIzjlb5h4FKH/view?usp=sharing", + " float sheenEnergyComp = 1.0 - 0.157 * max3( material.sheenColor );", + " outgoingLight = outgoingLight * sheenEnergyComp + sheenSpecular;", + " #endif", + " #ifdef USE_CLEARCOAT", + " float dotNVcc = saturate( dot( geometry.clearcoatNormal, geometry.viewDir ) );", + " vec3 Fcc = F_Schlick( material.clearcoatF0, material.clearcoatF90, dotNVcc );", + " outgoingLight = outgoingLight * ( 1.0 - material.clearcoat * Fcc ) + clearcoatSpecular * material.clearcoat;", + " #endif", + " #include ", + " #include ", + " #include ", + " #include ", + " #include ", + " #include ", + "}`;", + "", + "var uv_vertex_tile = `", + "", + " #ifdef UV_TILE", + " float col = mod(uvTile, tileCount.x);", + " float row = (tileCount.y - floor(uvTile / tileCount.x) - 1.0);", + " ", + " mat3 tileTransform = mat3(", + " 1.0 / tileCount.x, 0.0, 0.0,", + " 0.0, 1.0 / tileCount.y, 0.0, ", + " col / tileCount.x, row / tileCount.y, 1.0);", + " #else", + " mat3 tileTransform = mat3(1.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 1.0);", + " #endif", + "", + "#if defined( USE_UV ) || defined( USE_ANISOTROPY )", + "", + "vUv = (tileTransform *vec3( uv, 1 )).xy;", + "", + "#endif", + "#ifdef USE_MAP", + "", + "vMapUv = ( tileTransform * (mapTransform * vec3( MAP_UV, 1 ) )).xy;", + "", + "#endif", + "#ifdef USE_ALPHAMAP", + "", + "vAlphaMapUv = ( tileTransform * (alphaMapTransform * vec3( ALPHAMAP_UV, 1 ) )).xy;", + "", + "#endif", + "#ifdef USE_LIGHTMAP", + "", + "vLightMapUv = ( tileTransform * (lightMapTransform * vec3( LIGHTMAP_UV, 1 ) )).xy;", + "", + "#endif", + "#ifdef USE_AOMAP", + "", + "vAoMapUv = ( tileTransform * (aoMapTransform * vec3( AOMAP_UV, 1 ) )).xy;", + "", + "#endif", + "#ifdef USE_BUMPMAP", + "", + "vBumpMapUv = ( tileTransform * (bumpMapTransform * vec3( BUMPMAP_UV, 1 ) )).xy;", + "", + "#endif", + "#ifdef USE_NORMALMAP", + "", + "vNormalMapUv = ( tileTransform * (normalMapTransform * vec3( NORMALMAP_UV, 1 ) )).xy;", + "", + "#endif", + "#ifdef USE_DISPLACEMENTMAP", + "", + "vDisplacementMapUv = ( tileTransform * (displacementMapTransform * vec3( DISPLACEMENTMAP_UV, 1 ) )).xy;", + "", + "#endif", + "#ifdef USE_EMISSIVEMAP", + "", + "vEmissiveMapUv = ( tileTransform * (emissiveMapTransform * vec3( EMISSIVEMAP_UV, 1 ) )).xy;", + "", + "#endif", + "#ifdef USE_METALNESSMAP", + "", + "vMetalnessMapUv = ( tileTransform * (metalnessMapTransform * vec3( METALNESSMAP_UV, 1 ) )).xy;", + "", + "#endif", + "#ifdef USE_ROUGHNESSMAP", + "", + "vRoughnessMapUv = ( tileTransform * (roughnessMapTransform * vec3( ROUGHNESSMAP_UV, 1 ) )).xy;", + "", + "#endif", + "#ifdef USE_ANISOTROPYMAP", + "", + "vAnisotropyMapUv = ( tileTransform * (anisotropyMapTransform * vec3( ANISOTROPYMAP_UV, 1 ) )).xy;", + "", + "#endif", + "#ifdef USE_CLEARCOATMAP", + "", + "vClearcoatMapUv = ( tileTransform * (clearcoatMapTransform * vec3( CLEARCOATMAP_UV, 1 ) )).xy;", + "", + "#endif", + "#ifdef USE_CLEARCOAT_NORMALMAP", + "", + "vClearcoatNormalMapUv = ( tileTransform * (clearcoatNormalMapTransform * vec3( CLEARCOAT_NORMALMAP_UV, 1 ) )).xy;", + "", + "#endif", + "#ifdef USE_CLEARCOAT_ROUGHNESSMAP", + "", + "vClearcoatRoughnessMapUv = ( tileTransform * (clearcoatRoughnessMapTransform * vec3( CLEARCOAT_ROUGHNESSMAP_UV, 1 ) )).xy;", + "", + "#endif", + "#ifdef USE_IRIDESCENCEMAP", + "", + "vIridescenceMapUv = ( tileTransform * (iridescenceMapTransform * vec3( IRIDESCENCEMAP_UV, 1 ) )).xy;", + "", + "#endif", + "#ifdef USE_IRIDESCENCE_THICKNESSMAP", + "", + "vIridescenceThicknessMapUv = ( tileTransform * (iridescenceThicknessMapTransform * vec3( IRIDESCENCE_THICKNESSMAP_UV, 1 ) )).xy;", + "", + "#endif", + "#ifdef USE_SHEEN_COLORMAP", + "", + "vSheenColorMapUv = ( tileTransform * (sheenColorMapTransform * vec3( SHEEN_COLORMAP_UV, 1 ) )).xy;", + "", + "#endif", + "#ifdef USE_SHEEN_ROUGHNESSMAP", + "", + "vSheenRoughnessMapUv = ( tileTransform * (sheenRoughnessMapTransform * vec3( SHEEN_ROUGHNESSMAP_UV, 1 ) )).xy;", + "", + "#endif", + "#ifdef USE_SPECULARMAP", + "", + "vSpecularMapUv = ( tileTransform * (specularMapTransform * vec3( SPECULARMAP_UV, 1 ) )).xy;", + "", + "#endif", + "#ifdef USE_SPECULAR_COLORMAP", + "", + "vSpecularColorMapUv = ( tileTransform * (specularColorMapTransform * vec3( SPECULAR_COLORMAP_UV, 1 ) )).xy;", + "", + "#endif", + "#ifdef USE_SPECULAR_INTENSITYMAP", + "", + "vSpecularIntensityMapUv = ( tileTransform * (specularIntensityMapTransform * vec3( SPECULAR_INTENSITYMAP_UV, 1 ) )).xy;", + "", + "#endif", + "#ifdef USE_TRANSMISSIONMAP", + "", + "vTransmissionMapUv = ( tileTransform * transmissionMapTransform * vec3( TRANSMISSIONMAP_UV, 1 ) )).xy;", + "", + "#endif", + "#ifdef USE_THICKNESSMAP", + "", + "vThicknessMapUv = ( tileTransform * thicknessMapTransform * vec3( THICKNESSMAP_UV, 1 ) )).xy;", + "", + "#endif", + "`;", + "", + "var particle_vert = `", + "#include ", + "#include ", + "#include ", + "#include ", + "#include ", + "", + "attribute vec3 offset;", + "attribute float rotation;", + "attribute float size;", + "attribute float uvTile;", + "", + "#ifdef UV_TILE", + "uniform vec2 tileCount;", + "#endif", + "", + "void main() {", + "", + " ${uv_vertex_tile}", + "\t", + " vec2 alignedPosition = ( position.xy ) * size;", + " ", + " vec2 rotatedPosition;", + " rotatedPosition.x = cos( rotation ) * alignedPosition.x - sin( rotation ) * alignedPosition.y;", + " rotatedPosition.y = sin( rotation ) * alignedPosition.x + cos( rotation ) * alignedPosition.y;", + "#ifdef HORIZONTAL", + " vec4 mvPosition = modelMatrix * vec4( offset, 1.0 );", + " mvPosition.x += rotatedPosition.x;", + " mvPosition.z -= rotatedPosition.y;", + " mvPosition = viewMatrix * mvPosition;", + "#elif defined(VERTICAL)", + " vec4 mvPosition = modelMatrix * vec4( offset, 1.0 );", + " mvPosition.y += rotatedPosition.y;", + " mvPosition = viewMatrix * mvPosition;", + " mvPosition.x += rotatedPosition.x;", + "#else", + " vec4 mvPosition = modelViewMatrix * vec4( offset, 1.0 );", + " mvPosition.xy += rotatedPosition;", + "#endif", + "", + "\tvColor = color;", + "", + "\tgl_Position = projectionMatrix * mvPosition;", + "", + "\t#include ", + "\t#include ", + "", + "}", + "`;", + "", + "var local_particle_vert = `", + "#include ", + "#include ", + "#include ", + "#include ", + "#include ", + "", + "attribute vec3 offset;", + "attribute vec4 rotation;", + "attribute float size;", + "// attribute vec4 color;", + "attribute float uvTile;", + "", + "#ifdef UV_TILE", + "uniform vec2 tileCount;", + "#endif", + "", + "void main() {", + "", + " ${uv_vertex_tile}", + " ", + " float x2 = rotation.x + rotation.x, y2 = rotation.y + rotation.y, z2 = rotation.z + rotation.z;", + " float xx = rotation.x * x2, xy = rotation.x * y2, xz = rotation.x * z2;", + " float yy = rotation.y * y2, yz = rotation.y * z2, zz = rotation.z * z2;", + " float wx = rotation.w * x2, wy = rotation.w * y2, wz = rotation.w * z2;", + " float sx = size, sy = size, sz = size;", + " ", + " mat4 matrix = mat4(( 1.0 - ( yy + zz ) ) * sx, ( xy + wz ) * sx, ( xz - wy ) * sx, 0.0, // 1. column", + " ( xy - wz ) * sy, ( 1.0 - ( xx + zz ) ) * sy, ( yz + wx ) * sy, 0.0, // 2. column", + " ( xz + wy ) * sz, ( yz - wx ) * sz, ( 1.0 - ( xx + yy ) ) * sz, 0.0, // 3. column", + " offset.x, offset.y, offset.z, 1.0);", + " ", + " vec4 mvPosition = modelViewMatrix * (matrix * vec4( position, 1.0 ));", + "", + "\tvColor = color;", + "", + "\tgl_Position = projectionMatrix * mvPosition;", + "", + "\t#include ", + "\t#include ", + "", + "}", + "`;", + "", + "var local_particle_physics_vert = `", + "#define STANDARD", + "varying vec3 vViewPosition;", + "#ifdef USE_TRANSMISSION", + "\tvarying vec3 vWorldPosition;", + "#endif", + "#include ", + "#include ", + "", + "attribute vec3 offset;", + "attribute vec4 rotation;", + "attribute float size;", + "attribute float uvTile;", + "", + "#ifdef UV_TILE", + "uniform vec2 tileCount;", + "#endif", + "", + "#include ", + "#include ", + "#include ", + "#include ", + "#include ", + "#include ", + "#include ", + "#include ", + "#include ", + "", + "void main() {", + " ${uv_vertex_tile}", + "", + " float x2 = rotation.x + rotation.x, y2 = rotation.y + rotation.y, z2 = rotation.z + rotation.z;", + " float xx = rotation.x * x2, xy = rotation.x * y2, xz = rotation.x * z2;", + " float yy = rotation.y * y2, yz = rotation.y * z2, zz = rotation.z * z2;", + " float wx = rotation.w * x2, wy = rotation.w * y2, wz = rotation.w * z2;", + " float sx = size, sy = size, sz = size;", + "", + " mat4 particleMatrix = mat4(( 1.0 - ( yy + zz ) ) * sx, ( xy + wz ) * sx, ( xz - wy ) * sx, 0.0, // 1. column", + " ( xy - wz ) * sy, ( 1.0 - ( xx + zz ) ) * sy, ( yz + wx ) * sy, 0.0, // 2. column", + " ( xz + wy ) * sz, ( yz - wx ) * sz, ( 1.0 - ( xx + yy ) ) * sz, 0.0, // 3. column", + " offset.x, offset.y, offset.z, 1.0);", + "", + "\t#include ", + "\t#include ", + "\t#include ", + "\t#include ", + "\t#include ", + "\t#include ", + "", + "\t// replace defaultnormal_vertex", + "\tvec3 transformedNormal = objectNormal;", + " mat3 m = mat3( particleMatrix );", + " transformedNormal /= vec3( dot( m[ 0 ], m[ 0 ] ), dot( m[ 1 ], m[ 1 ] ), dot( m[ 2 ], m[ 2 ] ) );", + " transformedNormal = m * transformedNormal;", + " transformedNormal = normalMatrix * transformedNormal;", + " #ifdef FLIP_SIDED", + " transformedNormal = - transformedNormal;", + " #endif", + " #ifdef USE_TANGENT", + " vec3 transformedTangent = ( modelViewMatrix * vec4( objectTangent, 0.0 ) ).xyz;", + " #ifdef FLIP_SIDED", + " transformedTangent = - transformedTangent;", + " #endif", + " #endif", + "", + "\t#include ", + "\t#include ", + "\t#include ", + "\t#include ", + "\t#include ", + "", + "\t// replace include ", + " vec4 mvPosition = vec4( transformed, 1.0 );", + " mvPosition = modelViewMatrix * (particleMatrix * mvPosition);", + "\tgl_Position = projectionMatrix * mvPosition;", + "", + "\t#include ", + "\t#include ", + "\t", + "\tvViewPosition = - mvPosition.xyz;", + "\t", + "\t#include ", + "\t#include ", + "\t#include ", + "#ifdef USE_TRANSMISSION", + "\tvWorldPosition = worldPosition.xyz;", + "#endif", + "}", + "`;", + "", + "var stretched_bb_particle_vert = `", + "#include ", + "#include ", + "#include ", + "#include ", + "#include ", + "", + "attribute vec3 offset;", + "attribute float rotation;", + "attribute float size;", + "attribute vec4 velocity;", + "attribute float uvTile;", + "", + "#ifdef UV_TILE", + "uniform vec2 tileCount;", + "#endif", + "", + "uniform float speedFactor;", + "", + "void main() {", + "", + " ${uv_vertex_tile}", + " ", + " float lengthFactor = velocity.w;", + "#ifdef USE_SKEW", + " vec4 mvPosition = modelViewMatrix * vec4( offset, 1.0 );", + " vec3 viewVelocity = normalMatrix * velocity.xyz;", + "", + " vec3 scaledPos = vec3(position.xy * size, position.z);", + " float vlength = length(viewVelocity);", + " vec3 projVelocity = dot(scaledPos, viewVelocity) * viewVelocity / vlength;", + " mvPosition.xyz += scaledPos + projVelocity * (speedFactor / size + lengthFactor / vlength);", + "#else", + " vec4 mvPosition = modelViewMatrix * vec4( offset, 1.0 );", + " vec3 viewVelocity = normalMatrix * velocity.xyz;", + " float vlength = length(viewVelocity); ", + " mvPosition.xyz += position.y * normalize(cross(mvPosition.xyz, viewVelocity)) * size; // switch the cross to match unity implementation", + " mvPosition.xyz -= (position.x + 0.5) * viewVelocity * (1.0 + lengthFactor / vlength) * size; // minus position.x to match unity implementation", + "#endif", + "\tvColor = color;", + "\tgl_Position = projectionMatrix * mvPosition;", + "\t#include ", + "\t#include ", + "}", + "`;", + "", + "function getMaterialUVChannelName(value) {", + " if (value === 0)", + " return 'uv';", + " return `uv${value}`;", + "}", + "", + "new THREE.Vector3(0, 0, 1);", + "class SpriteBatch extends VFXBatch {", + " constructor(settings) {", + " super(settings);", + " this.vector_ = new THREE.Vector3();", + " this.vector2_ = new THREE.Vector3();", + " this.vector3_ = new THREE.Vector3();", + " this.quaternion_ = new THREE.Quaternion();", + " this.quaternion2_ = new THREE.Quaternion();", + " this.quaternion3_ = new THREE.Quaternion();", + " this.rotationMat_ = new THREE.Matrix3();", + " this.rotationMat2_ = new THREE.Matrix3();", + " this.maxParticles = 1000;", + " this.setupBuffers();", + " this.rebuildMaterial();", + " }", + " buildExpandableBuffers() {", + " this.offsetBuffer = new THREE.InstancedBufferAttribute(new Float32Array(this.maxParticles * 3), 3);", + " this.offsetBuffer.setUsage(THREE.DynamicDrawUsage);", + " this.geometry.setAttribute('offset', this.offsetBuffer);", + " this.colorBuffer = new THREE.InstancedBufferAttribute(new Float32Array(this.maxParticles * 4), 4);", + " this.colorBuffer.setUsage(THREE.DynamicDrawUsage);", + " this.geometry.setAttribute('color', this.colorBuffer);", + " if (this.settings.renderMode === RenderMode.Mesh) {", + " this.rotationBuffer = new THREE.InstancedBufferAttribute(new Float32Array(this.maxParticles * 4), 4);", + " this.rotationBuffer.setUsage(THREE.DynamicDrawUsage);", + " this.geometry.setAttribute('rotation', this.rotationBuffer);", + " }", + " else if (this.settings.renderMode === RenderMode.BillBoard ||", + " this.settings.renderMode === RenderMode.HorizontalBillBoard ||", + " this.settings.renderMode === RenderMode.VerticalBillBoard ||", + " this.settings.renderMode === RenderMode.StretchedBillBoard) {", + " this.rotationBuffer = new THREE.InstancedBufferAttribute(new Float32Array(this.maxParticles), 1);", + " this.rotationBuffer.setUsage(THREE.DynamicDrawUsage);", + " this.geometry.setAttribute('rotation', this.rotationBuffer);", + " }", + " this.sizeBuffer = new THREE.InstancedBufferAttribute(new Float32Array(this.maxParticles), 1);", + " this.sizeBuffer.setUsage(THREE.DynamicDrawUsage);", + " this.geometry.setAttribute('size', this.sizeBuffer);", + " this.uvTileBuffer = new THREE.InstancedBufferAttribute(new Float32Array(this.maxParticles), 1);", + " this.uvTileBuffer.setUsage(THREE.DynamicDrawUsage);", + " this.geometry.setAttribute('uvTile', this.uvTileBuffer);", + " if (this.settings.renderMode === RenderMode.StretchedBillBoard) {", + " this.velocityBuffer = new THREE.InstancedBufferAttribute(new Float32Array(this.maxParticles * 4), 4);", + " this.velocityBuffer.setUsage(THREE.DynamicDrawUsage);", + " this.geometry.setAttribute('velocity', this.velocityBuffer);", + " }", + " }", + " setupBuffers() {", + " if (this.geometry)", + " this.geometry.dispose();", + " this.geometry = new THREE.InstancedBufferGeometry();", + " this.geometry.setIndex(this.settings.instancingGeometry.getIndex());", + " if (this.settings.instancingGeometry.hasAttribute('normal')) {", + " this.geometry.setAttribute('normal', this.settings.instancingGeometry.getAttribute('normal'));", + " }", + " this.geometry.setAttribute('position', this.settings.instancingGeometry.getAttribute('position'));", + " this.geometry.setAttribute('uv', this.settings.instancingGeometry.getAttribute('uv'));", + " this.buildExpandableBuffers();", + " }", + " expandBuffers(target) {", + " while (target >= this.maxParticles) {", + " this.maxParticles *= 2;", + " }", + " this.setupBuffers();", + " }", + " rebuildMaterial() {", + " this.layers.mask = this.settings.layers.mask;", + " let uniforms;", + " const defines = {};", + " if (this.settings.material.type === 'MeshStandardMaterial' ||", + " this.settings.material.type === 'MeshPhysicalMaterial') {", + " const mat = this.settings.material;", + " uniforms = THREE.UniformsUtils.merge([", + " THREE.UniformsLib.common,", + " THREE.UniformsLib.envmap,", + " THREE.UniformsLib.aomap,", + " THREE.UniformsLib.lightmap,", + " THREE.UniformsLib.emissivemap,", + " THREE.UniformsLib.bumpmap,", + " THREE.UniformsLib.normalmap,", + " THREE.UniformsLib.displacementmap,", + " THREE.UniformsLib.roughnessmap,", + " THREE.UniformsLib.metalnessmap,", + " THREE.UniformsLib.fog,", + " THREE.UniformsLib.lights,", + " {", + " emissive: { value: new THREE.Color(0x000000) },", + " roughness: { value: 1.0 },", + " metalness: { value: 0.0 },", + " envMapIntensity: { value: 1 },", + " },", + " ]);", + " uniforms['diffuse'].value = mat.color;", + " uniforms['opacity'].value = mat.opacity;", + " uniforms['emissive'].value = mat.emissive;", + " uniforms['roughness'].value = mat.roughness;", + " uniforms['metalness'].value = mat.metalness;", + " if (mat.envMap) {", + " uniforms['envMap'].value = mat.envMap;", + " uniforms['envMapIntensity'].value = mat.envMapIntensity;", + " }", + " if (mat.normalMap) {", + " uniforms['normalMap'].value = mat.normalMap;", + " uniforms['normalScale'].value = mat.normalScale;", + " }", + " if (mat.roughnessMap) {", + " uniforms['roughnessMap'].value = mat.roughnessMap;", + " }", + " if (mat.metalnessMap) {", + " uniforms['metalnessMap'].value = mat.metalnessMap;", + " }", + " if (mat.map) {", + " uniforms['map'] = new THREE.Uniform(mat.map);", + " }", + " }", + " else {", + " uniforms = {};", + " uniforms['map'] = new THREE.Uniform(this.settings.material.map);", + " }", + " if (this.settings.material.alphaTest) {", + " defines['USE_ALPHATEST'] = '';", + " uniforms['alphaTest'] = new THREE.Uniform(this.settings.material.alphaTest);", + " }", + " defines['USE_UV'] = '';", + " const uTileCount = this.settings.uTileCount;", + " const vTileCount = this.settings.vTileCount;", + " if (uTileCount > 1 || vTileCount > 1) {", + " defines['UV_TILE'] = '';", + " uniforms['tileCount'] = new THREE.Uniform(new THREE.Vector2(uTileCount, vTileCount));", + " }", + " if (this.settings.material.defines &&", + " this.settings.material.defines['USE_COLOR_AS_ALPHA'] !== undefined) {", + " defines['USE_COLOR_AS_ALPHA'] = '';", + " }", + " if (this.settings.material.normalMap) {", + " defines['USE_NORMALMAP'] = '';", + " defines['NORMALMAP_UV'] = getMaterialUVChannelName(this.settings.material.normalMap.channel);", + " uniforms['normalMapTransform'] = new THREE.Uniform(new THREE.Matrix3().copy(this.settings.material.normalMap.matrix));", + " }", + " if (this.settings.material.map) {", + " defines['USE_MAP'] = '';", + " defines['MAP_UV'] = getMaterialUVChannelName(this.settings.material.map.channel);", + " uniforms['mapTransform'] = new THREE.Uniform(new THREE.Matrix3().copy(this.settings.material.map.matrix));", + " }", + " defines['USE_COLOR_ALPHA'] = '';", + " let needLights = false;", + " if (this.settings.renderMode === RenderMode.BillBoard ||", + " this.settings.renderMode === RenderMode.VerticalBillBoard ||", + " this.settings.renderMode === RenderMode.HorizontalBillBoard ||", + " this.settings.renderMode === RenderMode.Mesh) {", + " let vertexShader;", + " let fragmentShader;", + " if (this.settings.renderMode === RenderMode.Mesh) {", + " if (this.settings.material.type === 'MeshStandardMaterial' ||", + " this.settings.material.type === 'MeshPhysicalMaterial') {", + " defines['USE_COLOR'] = '';", + " vertexShader = local_particle_physics_vert;", + " fragmentShader = particle_physics_frag;", + " needLights = true;", + " }", + " else {", + " vertexShader = local_particle_vert;", + " fragmentShader = particle_frag;", + " }", + " }", + " else {", + " vertexShader = particle_vert;", + " fragmentShader = particle_frag;", + " }", + " if (this.settings.renderMode === RenderMode.VerticalBillBoard) {", + " defines['VERTICAL'] = '';", + " }", + " else if (this.settings.renderMode === RenderMode.HorizontalBillBoard) {", + " defines['HORIZONTAL'] = '';", + " }", + " this.material = new THREE.ShaderMaterial({", + " uniforms: uniforms,", + " defines: defines,", + " vertexShader: vertexShader,", + " fragmentShader: fragmentShader,", + " transparent: this.settings.material.transparent,", + " depthWrite: !this.settings.material.transparent,", + " blending: this.settings.material.blending,", + " side: this.settings.material.side,", + " alphaTest: this.settings.material.alphaTest,", + " lights: needLights,", + " });", + " }", + " else if (this.settings.renderMode === RenderMode.StretchedBillBoard) {", + " uniforms['speedFactor'] = new THREE.Uniform(1.0);", + " this.material = new THREE.ShaderMaterial({", + " uniforms: uniforms,", + " defines: defines,", + " vertexShader: stretched_bb_particle_vert,", + " fragmentShader: particle_frag,", + " transparent: this.settings.material.transparent,", + " depthWrite: !this.settings.material.transparent,", + " blending: this.settings.material.blending,", + " side: this.settings.material.side,", + " alphaTest: this.settings.material.alphaTest,", + " });", + " }", + " else {", + " throw new Error('render mode unavailable');", + " }", + " }", + " update() {", + " let index = 0;", + " let particleCount = 0;", + " this.systems.forEach((system) => {", + " particleCount += system.particleNum;", + " });", + " if (particleCount > this.maxParticles) {", + " this.expandBuffers(particleCount);", + " }", + " this.systems.forEach((system) => {", + " const particles = system.particles;", + " const particleNum = system.particleNum;", + " const rotation = this.quaternion2_;", + " const translation = this.vector2_;", + " const scale = this.vector3_;", + " system.emitter.matrixWorld.decompose(translation, rotation, scale);", + " this.rotationMat_.setFromMatrix4(system.emitter.matrixWorld);", + " for (let j = 0; j < particleNum; j++ , index++) {", + " const particle = particles[j];", + " if (this.settings.renderMode === RenderMode.Mesh) {", + " let q;", + " if (system.worldSpace) {", + " q = particle.rotation;", + " }", + " else {", + " let parentQ;", + " if (particle.parentMatrix) {", + " parentQ = this.quaternion3_.setFromRotationMatrix(particle.parentMatrix);", + " }", + " else {", + " parentQ = rotation;", + " }", + " q = this.quaternion_;", + " q.copy(particle.rotation).multiply(parentQ);", + " }", + " this.rotationBuffer.setXYZW(index, q.x, q.y, q.z, q.w);", + " }", + " else if (this.settings.renderMode === RenderMode.StretchedBillBoard ||", + " this.settings.renderMode === RenderMode.VerticalBillBoard ||", + " this.settings.renderMode === RenderMode.HorizontalBillBoard ||", + " this.settings.renderMode === RenderMode.BillBoard) {", + " this.rotationBuffer.setX(index, particle.rotation);", + " }", + " let vec;", + " if (system.worldSpace) {", + " vec = particle.position;", + " }", + " else {", + " vec = this.vector_;", + " if (particle.parentMatrix) {", + " vec.copy(particle.position).applyMatrix4(particle.parentMatrix);", + " }", + " else {", + " vec.copy(particle.position).applyMatrix4(system.emitter.matrixWorld);", + " }", + " }", + " this.offsetBuffer.setXYZ(index, vec.x, vec.y, vec.z);", + " this.colorBuffer.setXYZW(index, particle.color.x, particle.color.y, particle.color.z, particle.color.w);", + " if (system.worldSpace) {", + " this.sizeBuffer.setX(index, particle.size);", + " }", + " else {", + " if (particle.parentMatrix) {", + " this.sizeBuffer.setX(index, particle.size);", + " }", + " else {", + " this.sizeBuffer.setX(index, (particle.size * (Math.abs(scale.x) + Math.abs(scale.y) + Math.abs(scale.z))) / 3);", + " }", + " }", + " this.uvTileBuffer.setX(index, particle.uvTile);", + " if (this.settings.renderMode === RenderMode.StretchedBillBoard && this.velocityBuffer) {", + " let speedFactor = system.rendererEmitterSettings.speedFactor;", + " if (speedFactor === 0)", + " speedFactor = 0.001;", + " const lengthFactor = system.rendererEmitterSettings.lengthFactor;", + " let vec;", + " if (system.worldSpace) {", + " vec = particle.velocity;", + " }", + " else {", + " vec = this.vector_;", + " if (particle.parentMatrix) {", + " this.rotationMat2_.setFromMatrix4(particle.parentMatrix);", + " vec.copy(particle.velocity).applyMatrix3(this.rotationMat2_);", + " }", + " else {", + " vec.copy(particle.velocity).applyMatrix3(this.rotationMat_);", + " }", + " }", + " this.velocityBuffer.setXYZW(index, vec.x * speedFactor, vec.y * speedFactor, vec.z * speedFactor, lengthFactor);", + " }", + " }", + " });", + " this.geometry.instanceCount = index;", + " if (index > 0) {", + " this.offsetBuffer.clearUpdateRanges();", + " this.offsetBuffer.addUpdateRange(0, index * 3);", + " this.offsetBuffer.needsUpdate = true;", + " this.sizeBuffer.clearUpdateRanges();", + " this.sizeBuffer.addUpdateRange(0, index);", + " this.sizeBuffer.needsUpdate = true;", + " this.colorBuffer.clearUpdateRanges();", + " this.colorBuffer.addUpdateRange(0, index * 4);", + " this.colorBuffer.needsUpdate = true;", + " this.uvTileBuffer.clearUpdateRanges();", + " this.uvTileBuffer.addUpdateRange(0, index);", + " this.uvTileBuffer.needsUpdate = true;", + " if (this.settings.renderMode === RenderMode.StretchedBillBoard && this.velocityBuffer) {", + " this.velocityBuffer.clearUpdateRanges();", + " this.velocityBuffer.addUpdateRange(0, index * 4);", + " this.velocityBuffer.needsUpdate = true;", + " }", + " if (this.settings.renderMode === RenderMode.Mesh) {", + " this.rotationBuffer.clearUpdateRanges();", + " this.rotationBuffer.addUpdateRange(0, index * 4);", + " this.rotationBuffer.needsUpdate = true;", + " }", + " else if (this.settings.renderMode === RenderMode.StretchedBillBoard ||", + " this.settings.renderMode === RenderMode.HorizontalBillBoard ||", + " this.settings.renderMode === RenderMode.VerticalBillBoard ||", + " this.settings.renderMode === RenderMode.BillBoard) {", + " this.rotationBuffer.clearUpdateRanges();", + " this.rotationBuffer.addUpdateRange(0, index);", + " this.rotationBuffer.needsUpdate = true;", + " }", + " }", + " }", + " dispose() {", + " this.geometry.dispose();", + " }", + "}", + "", + "var trail_frag = `", + "", + "#include ", + "#include ", + "#include ", + "#include ", + "#include ", + "#include ", + "", + "uniform sampler2D alphaMap;", + "uniform float useAlphaMap;", + "uniform float visibility;", + "uniform float alphaTest;", + "uniform vec2 repeat;", + "", + "varying vec4 vColor;", + " ", + "void main() {", + " #include ", + " #include ", + "", + " vec4 c = vColor;", + " ", + " #ifdef USE_MAP", + " #ifdef USE_COLOR_AS_ALPHA", + " vec4 tex = texture2D( map, vUv * repeat );", + " c *= vec4(tex.rgb, tex.r);", + " #else", + " c *= texture2D( map, vUv * repeat );", + " #endif", + " #endif", + " if( useAlphaMap == 1. ) c.a *= texture2D( alphaMap, vUv * repeat ).a;", + " if( c.a < alphaTest ) discard;", + " gl_FragColor = c;", + "", + " #include ", + " #include ", + "}`;", + "", + "var trail_vert = `", + "#include ", + "#include ", + "#include ", + "#include ", + "#include ", + "#include ", + "", + "attribute vec3 previous;", + "attribute vec3 next;", + "attribute float side;", + "attribute float width;", + "", + "uniform vec2 resolution;", + "uniform float lineWidth;", + "uniform float sizeAttenuation;", + " ", + "vec2 fix(vec4 i, float aspect) {", + " vec2 res = i.xy / i.w;", + " res.x *= aspect;", + " return res;", + "}", + " ", + "void main() {", + "", + " ${uv_vertex_tile}", + " ", + " float aspect = resolution.x / resolution.y;", + "", + " vColor = color;", + "", + " mat4 m = projectionMatrix * modelViewMatrix;", + " vec4 finalPosition = m * vec4( position, 1.0 );", + " vec4 prevPos = m * vec4( previous, 1.0 );", + " vec4 nextPos = m * vec4( next, 1.0 );", + "", + " vec2 currentP = fix( finalPosition, aspect );", + " vec2 prevP = fix( prevPos, aspect );", + " vec2 nextP = fix( nextPos, aspect );", + "", + " float w = lineWidth * width;", + "", + " vec2 dir;", + " if( nextP == currentP ) dir = normalize( currentP - prevP );", + " else if( prevP == currentP ) dir = normalize( nextP - currentP );", + " else {", + " vec2 dir1 = normalize( currentP - prevP );", + " vec2 dir2 = normalize( nextP - currentP );", + " dir = normalize( dir1 + dir2 );", + "", + " vec2 perp = vec2( -dir1.y, dir1.x );", + " vec2 miter = vec2( -dir.y, dir.x );", + " //w = clamp( w / dot( miter, perp ), 0., 4., * lineWidth * width );", + "", + " }", + "", + " //vec2 normal = ( cross( vec3( dir, 0. ) vec3( 0., 0., 1. ) ) ).xy;", + " vec4 normal = vec4( -dir.y, dir.x, 0., 1. );", + " normal.xy *= .5 * w;", + " normal *= projectionMatrix;", + " if( sizeAttenuation == 0. ) {", + " normal.xy *= finalPosition.w;", + " normal.xy /= ( vec4( resolution, 0., 1. ) * projectionMatrix ).xy;", + " }", + "", + " finalPosition.xy += normal.xy * side;", + "", + " gl_Position = finalPosition;", + "", + "\t#include ", + "\t#include ", + "\t", + " vec4 mvPosition = modelViewMatrix * vec4( position, 1.0 );", + " ", + "\t#include ", + "}`;", + "", + "new THREE.Vector3(0, 0, 1);", + "class TrailBatch extends VFXBatch {", + " constructor(settings) {", + " super(settings);", + " this.vector_ = new THREE.Vector3();", + " this.vector2_ = new THREE.Vector3();", + " this.vector3_ = new THREE.Vector3();", + " this.quaternion_ = new THREE.Quaternion();", + " this.maxParticles = 10000;", + " this.setupBuffers();", + " this.rebuildMaterial();", + " }", + " setupBuffers() {", + " if (this.geometry)", + " this.geometry.dispose();", + " this.geometry = new THREE.BufferGeometry();", + " this.indexBuffer = new THREE.BufferAttribute(new Uint32Array(this.maxParticles * 6), 1);", + " this.indexBuffer.setUsage(THREE.DynamicDrawUsage);", + " this.geometry.setIndex(this.indexBuffer);", + " this.positionBuffer = new THREE.BufferAttribute(new Float32Array(this.maxParticles * 6), 3);", + " this.positionBuffer.setUsage(THREE.DynamicDrawUsage);", + " this.geometry.setAttribute('position', this.positionBuffer);", + " this.previousBuffer = new THREE.BufferAttribute(new Float32Array(this.maxParticles * 6), 3);", + " this.previousBuffer.setUsage(THREE.DynamicDrawUsage);", + " this.geometry.setAttribute('previous', this.previousBuffer);", + " this.nextBuffer = new THREE.BufferAttribute(new Float32Array(this.maxParticles * 6), 3);", + " this.nextBuffer.setUsage(THREE.DynamicDrawUsage);", + " this.geometry.setAttribute('next', this.nextBuffer);", + " this.widthBuffer = new THREE.BufferAttribute(new Float32Array(this.maxParticles * 2), 1);", + " this.widthBuffer.setUsage(THREE.DynamicDrawUsage);", + " this.geometry.setAttribute('width', this.widthBuffer);", + " this.sideBuffer = new THREE.BufferAttribute(new Float32Array(this.maxParticles * 2), 1);", + " this.sideBuffer.setUsage(THREE.DynamicDrawUsage);", + " this.geometry.setAttribute('side', this.sideBuffer);", + " this.uvBuffer = new THREE.BufferAttribute(new Float32Array(this.maxParticles * 4), 2);", + " this.uvBuffer.setUsage(THREE.DynamicDrawUsage);", + " this.geometry.setAttribute('uv', this.uvBuffer);", + " this.colorBuffer = new THREE.BufferAttribute(new Float32Array(this.maxParticles * 8), 4);", + " this.colorBuffer.setUsage(THREE.DynamicDrawUsage);", + " this.geometry.setAttribute('color', this.colorBuffer);", + " }", + " expandBuffers(target) {", + " while (target >= this.maxParticles) {", + " this.maxParticles *= 2;", + " }", + " this.setupBuffers();", + " }", + " rebuildMaterial() {", + " this.layers.mask = this.settings.layers.mask;", + " const uniforms = {", + " lineWidth: { value: 1 },", + " map: { value: null },", + " useMap: { value: 0 },", + " alphaMap: { value: null },", + " useAlphaMap: { value: 0 },", + " resolution: { value: new THREE.Vector2(1, 1) },", + " sizeAttenuation: { value: 1 },", + " visibility: { value: 1 },", + " alphaTest: { value: 0 },", + " repeat: { value: new THREE.Vector2(1, 1) },", + " };", + " const defines = {};", + " defines['USE_UV'] = '';", + " defines['USE_COLOR_ALPHA'] = '';", + " if (this.settings.material.map) {", + " defines['USE_MAP'] = '';", + " defines['MAP_UV'] = getMaterialUVChannelName(this.settings.material.map.channel);", + " uniforms['map'] = new THREE.Uniform(this.settings.material.map);", + " uniforms['mapTransform'] = new THREE.Uniform(new THREE.Matrix3().copy(this.settings.material.map.matrix));", + " }", + " if (this.settings.material.defines &&", + " this.settings.material.defines['USE_COLOR_AS_ALPHA'] !== undefined) {", + " defines['USE_COLOR_AS_ALPHA'] = '';", + " }", + " if (this.settings.renderMode === RenderMode.Trail) {", + " this.material = new THREE.ShaderMaterial({", + " uniforms: uniforms,", + " defines: defines,", + " vertexShader: trail_vert,", + " fragmentShader: trail_frag,", + " transparent: this.settings.material.transparent,", + " depthWrite: !this.settings.material.transparent,", + " side: this.settings.material.side,", + " blending: this.settings.material.blending || THREE.AdditiveBlending,", + " });", + " }", + " else {", + " throw new Error('render mode unavailable');", + " }", + " }", + " update() {", + " let index = 0;", + " let triangles = 0;", + " let particleCount = 0;", + " this.systems.forEach((system) => {", + " for (let j = 0; j < system.particleNum; j++) {", + " particleCount += system.particles[j].previous.length * 2;", + " }", + " });", + " if (particleCount > this.maxParticles) {", + " this.expandBuffers(particleCount);", + " }", + " this.systems.forEach((system) => {", + " const rotation = this.quaternion_;", + " const translation = this.vector2_;", + " const scale = this.vector3_;", + " system.emitter.matrixWorld.decompose(translation, rotation, scale);", + " const particles = system.particles;", + " const particleNum = system.particleNum;", + " const uTileCount = this.settings.uTileCount;", + " const vTileCount = this.settings.vTileCount;", + " const tileWidth = 1 / uTileCount;", + " const tileHeight = 1 / vTileCount;", + " for (let j = 0; j < particleNum; j++) {", + " const particle = particles[j];", + " const col = particle.uvTile % vTileCount;", + " const row = Math.floor(particle.uvTile / vTileCount + 0.001);", + " const iter = particle.previous.values();", + " let curIter = iter.next();", + " let previous = curIter.value;", + " let current = previous;", + " if (!curIter.done)", + " curIter = iter.next();", + " let next;", + " if (curIter.value !== undefined) {", + " next = curIter.value;", + " }", + " else {", + " next = current;", + " }", + " for (let i = 0; i < particle.previous.length; i++ , index += 2) {", + " this.positionBuffer.setXYZ(index, current.position.x, current.position.y, current.position.z);", + " this.positionBuffer.setXYZ(index + 1, current.position.x, current.position.y, current.position.z);", + " if (system.worldSpace) {", + " this.positionBuffer.setXYZ(index, current.position.x, current.position.y, current.position.z);", + " this.positionBuffer.setXYZ(index + 1, current.position.x, current.position.y, current.position.z);", + " }", + " else {", + " if (particle.parentMatrix) {", + " this.vector_.copy(current.position).applyMatrix4(particle.parentMatrix);", + " }", + " else {", + " this.vector_.copy(current.position).applyMatrix4(system.emitter.matrixWorld);", + " }", + " this.positionBuffer.setXYZ(index, this.vector_.x, this.vector_.y, this.vector_.z);", + " this.positionBuffer.setXYZ(index + 1, this.vector_.x, this.vector_.y, this.vector_.z);", + " }", + " if (system.worldSpace) {", + " this.previousBuffer.setXYZ(index, previous.position.x, previous.position.y, previous.position.z);", + " this.previousBuffer.setXYZ(index + 1, previous.position.x, previous.position.y, previous.position.z);", + " }", + " else {", + " if (particle.parentMatrix) {", + " this.vector_.copy(previous.position).applyMatrix4(particle.parentMatrix);", + " }", + " else {", + " this.vector_.copy(previous.position).applyMatrix4(system.emitter.matrixWorld);", + " }", + " this.previousBuffer.setXYZ(index, this.vector_.x, this.vector_.y, this.vector_.z);", + " this.previousBuffer.setXYZ(index + 1, this.vector_.x, this.vector_.y, this.vector_.z);", + " }", + " if (system.worldSpace) {", + " this.nextBuffer.setXYZ(index, next.position.x, next.position.y, next.position.z);", + " this.nextBuffer.setXYZ(index + 1, next.position.x, next.position.y, next.position.z);", + " }", + " else {", + " if (particle.parentMatrix) {", + " this.vector_.copy(next.position).applyMatrix4(particle.parentMatrix);", + " }", + " else {", + " this.vector_.copy(next.position).applyMatrix4(system.emitter.matrixWorld);", + " }", + " this.nextBuffer.setXYZ(index, this.vector_.x, this.vector_.y, this.vector_.z);", + " this.nextBuffer.setXYZ(index + 1, this.vector_.x, this.vector_.y, this.vector_.z);", + " }", + " this.sideBuffer.setX(index, -1);", + " this.sideBuffer.setX(index + 1, 1);", + " if (system.worldSpace) {", + " this.widthBuffer.setX(index, current.size);", + " this.widthBuffer.setX(index + 1, current.size);", + " }", + " else {", + " if (particle.parentMatrix) {", + " this.widthBuffer.setX(index, current.size);", + " this.widthBuffer.setX(index + 1, current.size);", + " }", + " else {", + " const objectScale = (Math.abs(scale.x) + Math.abs(scale.y) + Math.abs(scale.z)) / 3;", + " this.widthBuffer.setX(index, current.size * objectScale);", + " this.widthBuffer.setX(index + 1, current.size * objectScale);", + " }", + " }", + " this.uvBuffer.setXY(index, (i / particle.previous.length + col) * tileWidth, (vTileCount - row - 1) * tileHeight);", + " this.uvBuffer.setXY(index + 1, (i / particle.previous.length + col) * tileWidth, (vTileCount - row) * tileHeight);", + " this.colorBuffer.setXYZW(index, current.color.x, current.color.y, current.color.z, current.color.w);", + " this.colorBuffer.setXYZW(index + 1, current.color.x, current.color.y, current.color.z, current.color.w);", + " if (i + 1 < particle.previous.length) {", + " this.indexBuffer.setX(triangles * 3, index);", + " this.indexBuffer.setX(triangles * 3 + 1, index + 1);", + " this.indexBuffer.setX(triangles * 3 + 2, index + 2);", + " triangles++;", + " this.indexBuffer.setX(triangles * 3, index + 2);", + " this.indexBuffer.setX(triangles * 3 + 1, index + 1);", + " this.indexBuffer.setX(triangles * 3 + 2, index + 3);", + " triangles++;", + " }", + " previous = current;", + " current = next;", + " if (!curIter.done) {", + " curIter = iter.next();", + " if (curIter.value !== undefined) {", + " next = curIter.value;", + " }", + " }", + " }", + " }", + " });", + " this.positionBuffer.clearUpdateRanges();", + " this.positionBuffer.addUpdateRange(0, index * 3);", + " this.positionBuffer.needsUpdate = true;", + " this.previousBuffer.clearUpdateRanges();", + " this.previousBuffer.addUpdateRange(0, index * 3);", + " this.previousBuffer.needsUpdate = true;", + " this.nextBuffer.clearUpdateRanges();", + " this.nextBuffer.addUpdateRange(0, index * 3);", + " this.nextBuffer.needsUpdate = true;", + " this.sideBuffer.clearUpdateRanges();", + " this.sideBuffer.addUpdateRange(0, index);", + " this.sideBuffer.needsUpdate = true;", + " this.widthBuffer.clearUpdateRanges();", + " this.widthBuffer.addUpdateRange(0, index);", + " this.widthBuffer.needsUpdate = true;", + " this.uvBuffer.clearUpdateRanges();", + " this.uvBuffer.addUpdateRange(0, index * 2);", + " this.uvBuffer.needsUpdate = true;", + " this.colorBuffer.clearUpdateRanges();", + " this.colorBuffer.addUpdateRange(0, index * 4);", + " this.colorBuffer.needsUpdate = true;", + " this.indexBuffer.clearUpdateRanges();", + " this.indexBuffer.addUpdateRange(0, triangles * 3);", + " this.indexBuffer.needsUpdate = true;", + " this.geometry.setDrawRange(0, triangles * 3);", + " }", + " dispose() {", + " this.geometry.dispose();", + " }", + "}", + "", + "class BatchedRenderer extends THREE.Object3D {", + " constructor() {", + " super();", + " this.batches = [];", + " this.systemToBatchIndex = new Map();", + " this.type = 'BatchedRenderer';", + " }", + " static equals(a, b) {", + " return (a.material.side === b.material.side &&", + " a.material.blending === b.material.blending &&", + " a.material.transparent === b.material.transparent &&", + " a.material.type === b.material.type &&", + " a.material.alphaTest === b.material.alphaTest &&", + " a.material.map === b.material.map &&", + " a.renderMode === b.renderMode &&", + " a.uTileCount === b.uTileCount &&", + " a.vTileCount === b.vTileCount &&", + " a.instancingGeometry === b.instancingGeometry &&", + " a.renderOrder === b.renderOrder &&", + " a.layers.mask === b.layers.mask);", + " }", + " addSystem(system) {", + " system._renderer = this;", + " const settings = system.getRendererSettings();", + " for (let i = 0; i < this.batches.length; i++) {", + " if (BatchedRenderer.equals(this.batches[i].settings, settings)) {", + " this.batches[i].addSystem(system);", + " this.systemToBatchIndex.set(system, i);", + " return;", + " }", + " }", + " let batch;", + " switch (settings.renderMode) {", + " case RenderMode.Trail:", + " batch = new TrailBatch(settings);", + " break;", + " case RenderMode.Mesh:", + " case RenderMode.BillBoard:", + " case RenderMode.VerticalBillBoard:", + " case RenderMode.HorizontalBillBoard:", + " case RenderMode.StretchedBillBoard:", + " batch = new SpriteBatch(settings);", + " break;", + " }", + " batch.addSystem(system);", + " this.batches.push(batch);", + " this.systemToBatchIndex.set(system, this.batches.length - 1);", + " this.add(batch);", + " }", + " deleteSystem(system) {", + " const batchIndex = this.systemToBatchIndex.get(system);", + " if (batchIndex != undefined) {", + " this.batches[batchIndex].removeSystem(system);", + " this.systemToBatchIndex.delete(system);", + " }", + " }", + " updateSystem(system) {", + " this.deleteSystem(system);", + " this.addSystem(system);", + " }", + " update(delta) {", + " this.systemToBatchIndex.forEach((value, ps) => {", + " ps.update(delta);", + " });", + " for (let i = 0; i < this.batches.length; i++) {", + " this.batches[i].update();", + " }", + " }", + "}", + "", + "const BatchedParticleRenderer = BatchedRenderer;", + "", + "class QuarksLoader extends THREE.ObjectLoader {", + " constructor(manager) {", + " super(manager);", + " }", + " linkReference(object) {", + " const objectsMap = {};", + " object.traverse(function (child) {", + " objectsMap[child.uuid] = child;", + " });", + " object.traverse(function (child) {", + " if (child.type === 'ParticleEmitter') {", + " const system = child.system;", + " system.emitterShape;", + " for (let i = 0; i < system.behaviors.length; i++) {", + " if (system.behaviors[i] instanceof EmitSubParticleSystem) {", + " system.behaviors[i].subParticleSystem = objectsMap[system.behaviors[i].subParticleSystem];", + " }", + " }", + " }", + " });", + " }", + " parse(json, onLoad) {", + " const object = super.parse(json, onLoad);", + " this.linkReference(object);", + " return object;", + " }", + " parseObject(data, geometries, materials, textures, animations) {", + " let object;", + " function getGeometry(name) {", + " if (geometries[name] === undefined) {", + " console.warn('THREE.ObjectLoader: Undefined geometry', name);", + " }", + " return geometries[name];", + " }", + " function getMaterial(name) {", + " if (name === undefined)", + " return undefined;", + " if (Array.isArray(name)) {", + " const array = [];", + " for (let i = 0, l = name.length; i < l; i++) {", + " const uuid = name[i];", + " if (materials[uuid] === undefined) {", + " console.warn('THREE.ObjectLoader: Undefined material', uuid);", + " }", + " array.push(materials[uuid]);", + " }", + " return array;", + " }", + " if (materials[name] === undefined) {", + " console.warn('THREE.ObjectLoader: Undefined material', name);", + " }", + " return materials[name];", + " }", + " function getTexture(uuid) {", + " if (textures[uuid] === undefined) {", + " console.warn('THREE.ObjectLoader: Undefined texture', uuid);", + " }", + " return textures[uuid];", + " }", + " let geometry, material;", + " const meta = {", + " textures: textures,", + " geometries: geometries,", + " materials: materials,", + " };", + " const dependencies = {};", + " switch (data.type) {", + " case 'ParticleEmitter':", + " object = ParticleSystem.fromJSON(data.ps, meta, dependencies).emitter;", + " break;", + " case 'Scene':", + " object = new THREE.Scene();", + " if (data.background !== undefined) {", + " if (Number.isInteger(data.background)) {", + " object.background = new THREE.Color(data.background);", + " }", + " else {", + " object.background = getTexture(data.background);", + " }", + " }", + " if (data.environment !== undefined) {", + " object.environment = getTexture(data.environment);", + " }", + " if (data.fog !== undefined) {", + " if (data.fog.type === 'Fog') {", + " object.fog = new THREE.Fog(data.fog.color, data.fog.near, data.fog.far);", + " }", + " else if (data.fog.type === 'FogExp2') {", + " object.fog = new THREE.FogExp2(data.fog.color, data.fog.density);", + " }", + " }", + " if (data.backgroundBlurriness !== undefined)", + " object.backgroundBlurriness = data.backgroundBlurriness;", + " break;", + " case 'PerspectiveCamera':", + " object = new THREE.PerspectiveCamera(data.fov, data.aspect, data.near, data.far);", + " if (data.focus !== undefined)", + " object.focus = data.focus;", + " if (data.zoom !== undefined)", + " object.zoom = data.zoom;", + " if (data.filmGauge !== undefined)", + " object.filmGauge = data.filmGauge;", + " if (data.filmOffset !== undefined)", + " object.filmOffset = data.filmOffset;", + " if (data.view !== undefined)", + " object.view = Object.assign({}, data.view);", + " break;", + " case 'OrthographicCamera':", + " object = new THREE.OrthographicCamera(data.left, data.right, data.top, data.bottom, data.near, data.far);", + " if (data.zoom !== undefined)", + " object.zoom = data.zoom;", + " if (data.view !== undefined)", + " object.view = Object.assign({}, data.view);", + " break;", + " case 'AmbientLight':", + " object = new THREE.AmbientLight(data.color, data.intensity);", + " break;", + " case 'DirectionalLight':", + " object = new THREE.DirectionalLight(data.color, data.intensity);", + " break;", + " case 'PointLight':", + " object = new THREE.PointLight(data.color, data.intensity, data.distance, data.decay);", + " break;", + " case 'RectAreaLight':", + " object = new THREE.RectAreaLight(data.color, data.intensity, data.width, data.height);", + " break;", + " case 'SpotLight':", + " object = new THREE.SpotLight(data.color, data.intensity, data.distance, data.angle, data.penumbra, data.decay);", + " break;", + " case 'HemisphereLight':", + " object = new THREE.HemisphereLight(data.color, data.groundColor, data.intensity);", + " break;", + " case 'LightProbe':", + " object = new THREE.LightProbe().fromJSON(data);", + " break;", + " case 'SkinnedMesh':", + " geometry = getGeometry(data.geometry);", + " material = getMaterial(data.material);", + " object = new THREE.SkinnedMesh(geometry, material);", + " if (data.bindMode !== undefined)", + " object.bindMode = data.bindMode;", + " if (data.bindMatrix !== undefined)", + " object.bindMatrix.fromArray(data.bindMatrix);", + " if (data.skeleton !== undefined)", + " object.skeleton = data.skeleton;", + " break;", + " case 'Mesh':", + " geometry = getGeometry(data.geometry);", + " material = getMaterial(data.material);", + " object = new THREE.Mesh(geometry, material);", + " break;", + " case 'InstancedMesh': {", + " geometry = getGeometry(data.geometry);", + " material = getMaterial(data.material);", + " const count = data.count;", + " const instanceMatrix = data.instanceMatrix;", + " const instanceColor = data.instanceColor;", + " object = new THREE.InstancedMesh(geometry, material, count);", + " object.instanceMatrix = new THREE.InstancedBufferAttribute(new Float32Array(instanceMatrix.array), 16);", + " if (instanceColor !== undefined)", + " object.instanceColor = new THREE.InstancedBufferAttribute(new Float32Array(instanceColor.array), instanceColor.itemSize);", + " break;", + " }", + " case 'LOD':", + " object = new THREE.LOD();", + " break;", + " case 'Line':", + " object = new THREE.Line(getGeometry(data.geometry), getMaterial(data.material));", + " break;", + " case 'LineLoop':", + " object = new THREE.LineLoop(getGeometry(data.geometry), getMaterial(data.material));", + " break;", + " case 'LineSegments':", + " object = new THREE.LineSegments(getGeometry(data.geometry), getMaterial(data.material));", + " break;", + " case 'PointCloud':", + " case 'Points':", + " object = new THREE.Points(getGeometry(data.geometry), getMaterial(data.material));", + " break;", + " case 'Sprite':", + " object = new THREE.Sprite(getMaterial(data.material));", + " break;", + " case 'Group':", + " object = new THREE.Group();", + " break;", + " case 'Bone':", + " object = new THREE.Bone();", + " break;", + " default:", + " object = new THREE.Object3D();", + " }", + " object.uuid = data.uuid;", + " if (data.name !== undefined)", + " object.name = data.name;", + " if (data.matrix !== undefined) {", + " object.matrix.fromArray(data.matrix);", + " if (data.matrixAutoUpdate !== undefined)", + " object.matrixAutoUpdate = data.matrixAutoUpdate;", + " if (object.matrixAutoUpdate)", + " object.matrix.decompose(object.position, object.quaternion, object.scale);", + " }", + " else {", + " if (data.position !== undefined)", + " object.position.fromArray(data.position);", + " if (data.rotation !== undefined)", + " object.rotation.fromArray(data.rotation);", + " if (data.quaternion !== undefined)", + " object.quaternion.fromArray(data.quaternion);", + " if (data.scale !== undefined)", + " object.scale.fromArray(data.scale);", + " }", + " if (data.castShadow !== undefined)", + " object.castShadow = data.castShadow;", + " if (data.receiveShadow !== undefined)", + " object.receiveShadow = data.receiveShadow;", + " if (data.shadow) {", + " if (data.shadow.bias !== undefined)", + " object.shadow.bias = data.shadow.bias;", + " if (data.shadow.normalBias !== undefined)", + " object.normalBias = data.shadow.normalBias;", + " if (data.shadow.radius !== undefined)", + " object.radius = data.shadow.radius;", + " if (data.shadow.mapSize !== undefined)", + " object.mapSize.fromArray(data.shadow.mapSize);", + " if (data.shadow.camera !== undefined) {", + " object.camera = this.parseObject(data.shadow.camera);", + " }", + " }", + " if (data.visible !== undefined)", + " object.visible = data.visible;", + " if (data.frustumCulled !== undefined)", + " object.frustumCulled = data.frustumCulled;", + " if (data.renderOrder !== undefined)", + " object.renderOrder = data.renderOrder;", + " if (data.userData !== undefined)", + " object.userData = data.userData;", + " if (data.layers !== undefined)", + " object.layers.mask = data.layers;", + " if (data.children !== undefined) {", + " const children = data.children;", + " for (let i = 0; i < children.length; i++) {", + " object.add(this.parseObject(children[i], geometries, materials, textures, animations));", + " }", + " }", + " if (data.animations !== undefined) {", + " const objectAnimations = data.animations;", + " for (let i = 0; i < objectAnimations.length; i++) {", + " const uuid = objectAnimations[i];", + " object.animations.push(animations[uuid]);", + " }", + " }", + " if (data.type === 'LOD') {", + " if (data.autoUpdate !== undefined)", + " object.autoUpdate = data.autoUpdate;", + " const levels = data.levels;", + " for (let l = 0; l < levels.length; l++) {", + " const level = levels[l];", + " const child = object.getObjectByProperty('uuid', level.object);", + " if (child !== undefined) {", + " object.addLevel(child, level.distance);", + " }", + " }", + " }", + " return object;", + " }", + "}", + "", + "const Plugins = [];", + "function loadPlugin(plugin) {", + " const existing = Plugins.find((item) => item.id === plugin.id);", + " if (!existing) {", + " plugin.initialize();", + " for (const emitterShape of plugin.emitterShapes) {", + " if (!EmitterShapes[emitterShape.type]) {", + " EmitterShapes[emitterShape.type] = emitterShape;", + " }", + " }", + " for (const behavior of plugin.behaviors) {", + " if (!BehaviorTypes[behavior.type]) {", + " BehaviorTypes[behavior.type] = behavior;", + " }", + " }", + " }", + "}", + "function unloadPlugin(pluginId) {", + " const plugin = Plugins.find((item) => item.id === pluginId);", + " if (plugin) {", + " for (const emitterShape of plugin.emitterShapes) {", + " if (EmitterShapes[emitterShape.type]) {", + " delete EmitterShapes[emitterShape.type];", + " }", + " }", + " for (const behavior of plugin.behaviors) {", + " if (BehaviorTypes[behavior.type]) {", + " delete BehaviorTypes[behavior.type];", + " }", + " }", + " }", + "}", + "", + "let NodeValueType = void 0;", + "(function (NodeValueType) {", + " NodeValueType[NodeValueType[\"Number\"] = 0] = \"Number\";", + " NodeValueType[NodeValueType[\"Vec2\"] = 1] = \"Vec2\";", + " NodeValueType[NodeValueType[\"Vec3\"] = 2] = \"Vec3\";", + " NodeValueType[NodeValueType[\"Vec4\"] = 3] = \"Vec4\";", + " NodeValueType[NodeValueType[\"Boolean\"] = 4] = \"Boolean\";", + " NodeValueType[NodeValueType[\"AnyType\"] = 5] = \"AnyType\";", + " NodeValueType[NodeValueType[\"NullableAnyType\"] = 6] = \"NullableAnyType\";", + " NodeValueType[NodeValueType[\"EventStream\"] = 7] = \"EventStream\";", + "})(NodeValueType || (NodeValueType = {}));", + "const genDefaultForNodeValueType = (type) => {", + " switch (type) {", + " case NodeValueType.Boolean:", + " return false;", + " case NodeValueType.Number:", + " return 0;", + " case NodeValueType.Vec2:", + " return new THREE.Vector2();", + " case NodeValueType.Vec3:", + " return new THREE.Vector3();", + " case NodeValueType.Vec4:", + " return new THREE.Vector4();", + " case NodeValueType.AnyType:", + " return 0;", + " case NodeValueType.NullableAnyType:", + " return undefined;", + " }", + "};", + "", + "class Node {", + " constructor(type, signatureIndex = -1, data = {}) {", + " this.inputs = [];", + " this.outputs = [];", + " this.signatureIndex = -1;", + " this.position = new THREE.Vector2();", + " this.outputValues = [];", + " this.id = '' + Math.round(Math.random() * 100000);", + " this.type = type;", + " this.signatureIndex = signatureIndex;", + " this.data = data;", + " const realIndex = signatureIndex === -1 ? 0 : signatureIndex;", + " for (let i = 0; i < type.nodeTypeSignatures[realIndex].inputTypes.length; i++) {", + " this.inputs.push(undefined);", + " }", + " for (let i = 0; i < type.nodeTypeSignatures[realIndex].outputTypes.length; i++) {", + " this.outputs.push([]);", + " this.outputValues.push(genDefaultForNodeValueType(type.nodeTypeSignatures[realIndex].outputTypes[i]));", + " }", + " }", + " get inputTypes() {", + " const signatureIndex = this.signatureIndex === -1 ? 0 : this.signatureIndex;", + " return this.type.nodeTypeSignatures[signatureIndex].inputTypes;", + " }", + " get outputTypes() {", + " const signatureIndex = this.signatureIndex === -1 ? 0 : this.signatureIndex;", + " return this.type.nodeTypeSignatures[signatureIndex].outputTypes;", + " }", + " func(context, inputs, outputs) {", + " const signatureIndex = this.signatureIndex === -1 ? 0 : this.signatureIndex;", + " this.type.nodeTypeSignatures[signatureIndex].func(context, this.data, inputs, outputs);", + " }", + "}", + "class Wire {", + " constructor(input, inputIndex, output, outputIndex) {", + " this.input = input;", + " this.inputIndex = inputIndex;", + " this.input.outputs[inputIndex].push(this);", + " this.output = output;", + " this.outputIndex = outputIndex;", + " this.output.inputs[outputIndex] = this;", + " }", + "}", + "", + "class BaseCompiler {", + " constructor() {", + " this.visited = new Set();", + " BaseCompiler.Instance = this;", + " }", + " buildExecutionOrder(graph, context) {", + " graph.nodesInOrder.length = 0;", + " this.visited.clear();", + " for (let i = 0; i < graph.outputNodes.length; i++) {", + " const node = graph.outputNodes[i];", + " if (node.inputs[0] !== undefined) {", + " this._traverse(node, graph, context);", + " }", + " }", + " graph.compiled = true;", + " }", + " _traverse(node, graph, context) {", + " this.visited.add(node.id);", + " for (let i = 0; i < node.inputs.length; i++) {", + " if (node.inputs[i] instanceof Wire) {", + " const inputNode = node.inputs[i].input;", + " if (!this.visited.has(inputNode.id)) {", + " this._traverse(inputNode, graph, context);", + " }", + " }", + " else if (node.inputs[i] !== undefined);", + " else;", + " }", + " graph.nodesInOrder.push(node);", + " }", + "}", + "", + "class Interpreter extends BaseCompiler {", + " constructor() {", + " super();", + " }", + " executeCompiledGraph(graph, context) {", + " const nodes = graph.nodesInOrder;", + " for (let i = 0; i < nodes.length; i++) {", + " const inputValues = [];", + " const node = nodes[i];", + " for (let j = 0; j < node.inputs.length; j++) {", + " if (node.inputs[j] instanceof Wire) {", + " inputValues.push(node.inputs[j].input.outputValues[node.inputs[j].inputIndex]);", + " }", + " else if (node.inputs[j] !== undefined) {", + " inputValues.push(node.inputs[j].getValue(context));", + " }", + " else {", + " inputValues.push(undefined);", + " }", + " }", + " node.func(context, inputValues, node.outputValues);", + " }", + " }", + " run(graph, context) {", + " if (!graph.compiled) {", + " this.buildExecutionOrder(graph, context);", + " }", + " this.executeCompiledGraph(graph, context);", + " }", + "}", + "", + "class NodeType {", + " constructor(name) {", + " this.nodeTypeSignatures = [];", + " this.name = name;", + " }", + " addSignature(inputTypes, outputTypes, func) {", + " this.nodeTypeSignatures.push({", + " inputTypes: inputTypes,", + " outputTypes: outputTypes,", + " func: func,", + " });", + " }", + "}", + "class GraphNodeType extends NodeType {", + " constructor(nodeGraph) {", + " const inputTypes = [];", + " for (let i = 0; i < nodeGraph.inputNodes.length; i++) {", + " if (nodeGraph.inputNodes[i].type.name === 'input') {", + " inputTypes.push(nodeGraph.inputNodes[i].data.type);", + " }", + " }", + " const outputTypes = [];", + " for (let i = 0; i < nodeGraph.outputNodes.length; i++) {", + " if (nodeGraph.outputNodes[i].type.name === 'output') {", + " outputTypes.push(nodeGraph.outputNodes[i].data.type);", + " }", + " }", + " super(nodeGraph.name);", + " this.addSignature(inputTypes, outputTypes, (context, data, inputs, outputs) => {", + " context.inputs = inputs;", + " context.outputs = outputs;", + " Interpreter.Instance.run(nodeGraph, context);", + " });", + " this.nodeGraph = nodeGraph;", + " }", + "}", + "", + "const NodeTypes = {};", + "const addNode = new NodeType('add');", + "addNode.addSignature([NodeValueType.Number, NodeValueType.Number], [NodeValueType.Number], (context, data, inputs, outputs) => {", + " outputs[0] = inputs[0] + inputs[1];", + "});", + "addNode.addSignature([NodeValueType.Vec2, NodeValueType.Vec2], [NodeValueType.Vec2], (context, data, inputs, outputs) => {", + " outputs[0].addVectors(inputs[0], inputs[1]);", + "});", + "addNode.addSignature([NodeValueType.Vec3, NodeValueType.Vec3], [NodeValueType.Vec3], (context, data, inputs, outputs) => {", + " outputs[0].addVectors(inputs[0], inputs[1]);", + "});", + "addNode.addSignature([NodeValueType.Vec4, NodeValueType.Vec4], [NodeValueType.Vec4], (context, data, inputs, outputs) => {", + " outputs[0].addVectors(inputs[0], inputs[1]);", + "});", + "NodeTypes['add'] = addNode;", + "const subNode = new NodeType('sub');", + "subNode.addSignature([NodeValueType.Number, NodeValueType.Number], [NodeValueType.Number], (context, data, inputs, outputs) => {", + " outputs[0] = inputs[0] - inputs[1];", + "});", + "subNode.addSignature([NodeValueType.Vec2, NodeValueType.Vec2], [NodeValueType.Vec2], (context, data, inputs, outputs) => {", + " outputs[0].subVectors(inputs[0], inputs[1]);", + "});", + "subNode.addSignature([NodeValueType.Vec3, NodeValueType.Vec3], [NodeValueType.Vec3], (context, data, inputs, outputs) => {", + " outputs[0].subVectors(inputs[0], inputs[1]);", + "});", + "subNode.addSignature([NodeValueType.Vec4, NodeValueType.Vec4], [NodeValueType.Vec4], (context, data, inputs, outputs) => {", + " outputs[0].subVectors(inputs[0], inputs[1]);", + "});", + "NodeTypes['sub'] = subNode;", + "const mulNode = new NodeType('mul');", + "mulNode.addSignature([NodeValueType.Number, NodeValueType.Number], [NodeValueType.Number], (context, data, inputs, outputs) => {", + " outputs[0] = inputs[0] * inputs[1];", + "});", + "mulNode.addSignature([NodeValueType.Vec2, NodeValueType.Number], [NodeValueType.Vec2], (context, data, inputs, outputs) => {", + " outputs[0].copy(inputs[0]).multiplyScalar(inputs[1]);", + "});", + "mulNode.addSignature([NodeValueType.Vec3, NodeValueType.Number], [NodeValueType.Vec3], (context, data, inputs, outputs) => {", + " outputs[0].copy(inputs[0]).multiplyScalar(inputs[1]);", + "});", + "mulNode.addSignature([NodeValueType.Vec4, NodeValueType.Number], [NodeValueType.Vec4], (context, data, inputs, outputs) => {", + " outputs[0].copy(inputs[0]).multiplyScalar(inputs[1]);", + "});", + "NodeTypes['mul'] = mulNode;", + "const divNode = new NodeType('div');", + "divNode.addSignature([NodeValueType.Number, NodeValueType.Number], [NodeValueType.Number], (context, data, inputs, outputs) => {", + " outputs[0] = inputs[0] / inputs[1];", + "});", + "divNode.addSignature([NodeValueType.Vec2, NodeValueType.Number], [NodeValueType.Vec2], (context, data, inputs, outputs) => {", + " outputs[0].copy(inputs[0]).divideScalar(inputs[1]);", + "});", + "divNode.addSignature([NodeValueType.Vec3, NodeValueType.Number], [NodeValueType.Vec3], (context, data, inputs, outputs) => {", + " outputs[0].copy(inputs[0]).divideScalar(inputs[1]);", + "});", + "divNode.addSignature([NodeValueType.Vec4, NodeValueType.Number], [NodeValueType.Vec4], (context, data, inputs, outputs) => {", + " outputs[0].copy(inputs[0]).divideScalar(inputs[1]);", + "});", + "NodeTypes['div'] = divNode;", + "const sinNode = new NodeType('sin');", + "sinNode.addSignature([NodeValueType.Number], [NodeValueType.Number], (context, data, inputs, outputs) => {", + " outputs[0] = Math.sin(inputs[0]);", + "});", + "NodeTypes['sin'] = sinNode;", + "const cosNode = new NodeType('cos');", + "cosNode.addSignature([NodeValueType.Number], [NodeValueType.Number], (context, data, inputs, outputs) => {", + " outputs[0] = Math.cos(inputs[0]);", + "});", + "NodeTypes['cos'] = cosNode;", + "const tanNode = new NodeType('tan');", + "tanNode.addSignature([NodeValueType.Number], [NodeValueType.Number], (context, data, inputs, outputs) => {", + " outputs[0] = Math.tan(inputs[0]);", + "});", + "NodeTypes['tan'] = tanNode;", + "const absNode = new NodeType('abs');", + "absNode.addSignature([NodeValueType.Number], [NodeValueType.Number], (context, data, inputs, outputs) => {", + " outputs[0] = Math.abs(inputs[0]);", + "});", + "NodeTypes['abs'] = absNode;", + "const minNode = new NodeType('min');", + "minNode.addSignature([NodeValueType.Number, NodeValueType.Number], [NodeValueType.Number], (context, data, inputs, outputs) => {", + " outputs[0] = Math.min(inputs[0], inputs[1]);", + "});", + "NodeTypes['min'] = minNode;", + "const maxNode = new NodeType('max');", + "maxNode.addSignature([NodeValueType.Number, NodeValueType.Number], [NodeValueType.Number], (context, data, inputs, outputs) => {", + " outputs[0] = Math.max(inputs[0], inputs[1]);", + "});", + "NodeTypes['max'] = maxNode;", + "const dot = new NodeType('dot');", + "dot.addSignature([NodeValueType.Vec2, NodeValueType.Vec2], [NodeValueType.Number], (context, data, inputs, outputs) => {", + " outputs[0] = inputs[0].dot(inputs[1]);", + "});", + "dot.addSignature([NodeValueType.Vec3, NodeValueType.Vec3], [NodeValueType.Number], (context, data, inputs, outputs) => {", + " outputs[0] = inputs[0].dot(inputs[1]);", + "});", + "dot.addSignature([NodeValueType.Vec4, NodeValueType.Vec4], [NodeValueType.Number], (context, data, inputs, outputs) => {", + " outputs[0] = inputs[0].dot(inputs[1]);", + "});", + "NodeTypes['dot'] = dot;", + "const cross = new NodeType('cross');", + "cross.addSignature([NodeValueType.Vec3, NodeValueType.Vec3], [NodeValueType.Vec3], (context, data, inputs, outputs) => {", + " outputs[0].crossVectors(inputs[0], inputs[1]);", + "});", + "NodeTypes['cross'] = cross;", + "const length = new NodeType('length');", + "length.addSignature([NodeValueType.Vec2], [NodeValueType.Number], (context, data, inputs, outputs) => {", + " outputs[0] = inputs[0].length();", + "});", + "length.addSignature([NodeValueType.Vec3], [NodeValueType.Number], (context, data, inputs, outputs) => {", + " outputs[0] = inputs[0].length();", + "});", + "length.addSignature([NodeValueType.Vec4], [NodeValueType.Number], (context, data, inputs, outputs) => {", + " outputs[0] = inputs[0].length();", + "});", + "NodeTypes['length'] = length;", + "const lengthSq = new NodeType('lengthSq');", + "lengthSq.addSignature([NodeValueType.Vec2], [NodeValueType.Number], (context, data, inputs, outputs) => {", + " outputs[0] = inputs[0].lengthSq();", + "});", + "lengthSq.addSignature([NodeValueType.Vec3], [NodeValueType.Number], (context, data, inputs, outputs) => {", + " outputs[0] = inputs[0].lengthSq();", + "});", + "lengthSq.addSignature([NodeValueType.Vec4], [NodeValueType.Number], (context, data, inputs, outputs) => {", + " outputs[0] = inputs[0].lengthSq();", + "});", + "NodeTypes['lengthSq'] = lengthSq;", + "const normalize = new NodeType('normalize');", + "normalize.addSignature([NodeValueType.Vec2], [NodeValueType.Vec2], (context, data, inputs, outputs) => {", + " outputs[0].copy(inputs[0]).normalize();", + "});", + "normalize.addSignature([NodeValueType.Vec3], [NodeValueType.Vec3], (context, data, inputs, outputs) => {", + " outputs[0].copy(inputs[0]).normalize();", + "});", + "normalize.addSignature([NodeValueType.Vec4], [NodeValueType.Vec4], (context, data, inputs, outputs) => {", + " outputs[0].copy(inputs[0]).normalize();", + "});", + "NodeTypes['normalize'] = normalize;", + "const distance = new NodeType('distance');", + "distance.addSignature([NodeValueType.Vec2, NodeValueType.Vec2], [NodeValueType.Number], (context, data, inputs, outputs) => {", + " outputs[0] = inputs[0].distanceTo(inputs[1]);", + "});", + "distance.addSignature([NodeValueType.Vec3, NodeValueType.Vec3], [NodeValueType.Number], (context, data, inputs, outputs) => {", + " outputs[0] = inputs[0].distanceTo(inputs[1]);", + "});", + "NodeTypes['distance'] = distance;", + "const andNode = new NodeType('and');", + "andNode.addSignature([NodeValueType.Boolean, NodeValueType.Boolean], [NodeValueType.Boolean], (context, data, inputs, outputs) => {", + " outputs[0] = inputs[0] && inputs[1];", + "});", + "NodeTypes['and'] = andNode;", + "const orNode = new NodeType('or');", + "orNode.addSignature([NodeValueType.Boolean, NodeValueType.Boolean], [NodeValueType.Boolean], (context, data, inputs, outputs) => {", + " outputs[0] = inputs[0] || inputs[1];", + "});", + "NodeTypes['or'] = orNode;", + "const notNode = new NodeType('not');", + "notNode.addSignature([NodeValueType.Boolean], [NodeValueType.Boolean], (context, data, inputs, outputs) => {", + " outputs[0] = !inputs[0];", + "});", + "NodeTypes['not'] = notNode;", + "const equalNode = new NodeType('equal');", + "equalNode.addSignature([NodeValueType.Number, NodeValueType.Number], [NodeValueType.Boolean], (context, data, inputs, outputs) => {", + " outputs[0] = inputs[0] === inputs[1];", + "});", + "equalNode.addSignature([NodeValueType.Vec2, NodeValueType.Vec2], [NodeValueType.Boolean], (context, data, inputs, outputs) => {", + " outputs[0] = inputs[0].equals(inputs[1]);", + "});", + "equalNode.addSignature([NodeValueType.Vec3, NodeValueType.Vec3], [NodeValueType.Boolean], (context, data, inputs, outputs) => {", + " outputs[0] = inputs[0].equals(inputs[1]);", + "});", + "equalNode.addSignature([NodeValueType.Vec4, NodeValueType.Vec4], [NodeValueType.Boolean], (context, data, inputs, outputs) => {", + " outputs[0] = inputs[0].equals(inputs[1]);", + "});", + "NodeTypes['equal'] = equalNode;", + "const lessThanNode = new NodeType('lessThan');", + "lessThanNode.addSignature([NodeValueType.Number, NodeValueType.Number], [NodeValueType.Boolean], (context, data, inputs, outputs) => {", + " outputs[0] = inputs[0] < inputs[1];", + "});", + "NodeTypes['lessThan'] = lessThanNode;", + "const greaterThanNode = new NodeType('greaterThan');", + "greaterThanNode.addSignature([NodeValueType.Number, NodeValueType.Number], [NodeValueType.Boolean], (context, data, inputs, outputs) => {", + " outputs[0] = inputs[0] > inputs[1];", + "});", + "NodeTypes['greaterThan'] = greaterThanNode;", + "const lessThanOrEqualNode = new NodeType('lessThanOrEqual');", + "lessThanOrEqualNode.addSignature([NodeValueType.Number, NodeValueType.Number], [NodeValueType.Boolean], (context, data, inputs, outputs) => {", + " outputs[0] = inputs[0] <= inputs[1];", + "});", + "NodeTypes['lessThanOrEqual'] = lessThanOrEqualNode;", + "const greaterThanOrEqualNode = new NodeType('greaterThanOrEqual');", + "greaterThanOrEqualNode.addSignature([NodeValueType.Number, NodeValueType.Number], [NodeValueType.Boolean], (context, data, inputs, outputs) => {", + " outputs[0] = inputs[0] >= inputs[1];", + "});", + "NodeTypes['greaterThanOrEqual'] = greaterThanOrEqualNode;", + "const ifNode = new NodeType('if');", + "ifNode.addSignature([NodeValueType.Boolean, NodeValueType.AnyType, NodeValueType.AnyType], [NodeValueType.AnyType], (context, data, inputs, outputs) => {", + " outputs[0] = inputs[0] ? inputs[1] : inputs[2];", + "});", + "NodeTypes['if'] = ifNode;", + "const numberNode = new NodeType('number');", + "numberNode.addSignature([], [NodeValueType.Number], (context, data, inputs, outputs) => {", + " outputs[0] = data.value;", + "});", + "NodeTypes['number'] = numberNode;", + "const vec2Node = new NodeType('vec2');", + "vec2Node.addSignature([NodeValueType.Number, NodeValueType.Number], [NodeValueType.Vec2], (context, data, inputs, outputs) => {", + " outputs[0].x = inputs[0];", + " outputs[0].y = inputs[1];", + "});", + "NodeTypes['vec2'] = vec2Node;", + "const vec3Node = new NodeType('vec3');", + "vec3Node.addSignature([NodeValueType.Number, NodeValueType.Number, NodeValueType.Number], [NodeValueType.Vec3], (context, data, inputs, outputs) => {", + " outputs[0].x = inputs[0];", + " outputs[0].y = inputs[1];", + " outputs[0].z = inputs[2];", + "});", + "NodeTypes['vec3'] = vec3Node;", + "const vec4Node = new NodeType('vec4');", + "vec4Node.addSignature([NodeValueType.Number, NodeValueType.Number, NodeValueType.Number, NodeValueType.Number], [NodeValueType.Vec4], (context, data, inputs, outputs) => {", + " outputs[0].x = inputs[0];", + " outputs[0].y = inputs[1];", + " outputs[0].z = inputs[2];", + " outputs[0].w = inputs[3];", + "});", + "NodeTypes['vec4'] = vec4Node;", + "const splitVec2Node = new NodeType('splitVec2');", + "splitVec2Node.addSignature([NodeValueType.Vec2], [NodeValueType.Number, NodeValueType.Number], (context, data, inputs, outputs) => {", + " outputs[0] = inputs[0].x;", + " outputs[1] = inputs[0].y;", + "});", + "NodeTypes['splitVec2'] = splitVec2Node;", + "const splitVec3Node = new NodeType('splitVec3');", + "splitVec3Node.addSignature([NodeValueType.Vec3], [NodeValueType.Number, NodeValueType.Number, NodeValueType.Number], (context, data, inputs, outputs) => {", + " outputs[0] = inputs[0].x;", + " outputs[1] = inputs[0].y;", + " outputs[2] = inputs[0].z;", + "});", + "NodeTypes['splitVec3'] = splitVec3Node;", + "const splitVec4Node = new NodeType('splitVec4');", + "splitVec4Node.addSignature([NodeValueType.Vec4], [NodeValueType.Number, NodeValueType.Number, NodeValueType.Number, NodeValueType.Number], (context, data, inputs, outputs) => {", + " outputs[0] = inputs[0].x;", + " outputs[1] = inputs[0].y;", + " outputs[2] = inputs[0].z;", + " outputs[3] = inputs[0].w;", + "});", + "NodeTypes['splitVec4'] = splitVec4Node;", + "const boolNode = new NodeType('bool');", + "boolNode.addSignature([], [NodeValueType.Boolean], (context, data, inputs, outputs) => {", + " outputs[0] = data.value;", + "});", + "NodeTypes['bool'] = boolNode;", + "const particlePropertyNode = new NodeType('particleProperty');", + "particlePropertyNode.addSignature([NodeValueType.NullableAnyType], [NodeValueType.NullableAnyType], (context, data, inputs, outputs) => {", + " if (inputs[0] !== undefined) {", + " if (typeof inputs[0] === 'object') {", + " context.particle[data.property].copy(inputs[0]);", + " }", + " else {", + " context.particle[data.property] = inputs[0];", + " }", + " }", + " if (context.particle[data.property] !== undefined) {", + " if (typeof outputs[0] === 'object') {", + " outputs[0].copy(context.particle[data.property]);", + " }", + " else {", + " outputs[0] = context.particle[data.property];", + " }", + " }", + "});", + "NodeTypes['particleProperty'] = particlePropertyNode;", + "const emitNode = new NodeType('emit');", + "emitNode.addSignature([NodeValueType.EventStream], [], (context, data, inputs, outputs) => {", + " const arr = inputs[0];", + " for (let i = 0; i < arr.length; i++) {", + " context.signal(i, arr[i]);", + " }", + "});", + "NodeTypes['emit'] = emitNode;", + "const graphPropertyNode = new NodeType('graphProperty');", + "graphPropertyNode.addSignature([NodeValueType.NullableAnyType], [NodeValueType.NullableAnyType], (context, data, inputs, outputs) => {", + " if (inputs[0] !== undefined) {", + " if (typeof inputs[0] === 'object') {", + " context.graph[data.property].copy(inputs[0]);", + " }", + " else {", + " context.graph[data.property] = inputs[0];", + " }", + " }", + " if (context.graph[data.property] !== undefined) {", + " if (typeof outputs[0] === 'object') {", + " outputs[0].copy(context.graph[data.property]);", + " }", + " else {", + " outputs[0] = context.graph[data.property];", + " }", + " }", + "});", + "NodeTypes['graphProperty'] = graphPropertyNode;", + "const startEventNode = new NodeType('startEvent');", + "startEventNode.addSignature([], [NodeValueType.EventStream], (context, data, inputs, outputs) => {", + " outputs[0] = [{ type: 'start' }];", + "});", + "NodeTypes['startEvent'] = startEventNode;", + "const repeaterNode = new NodeType('repeater');", + "repeaterNode.addSignature([NodeValueType.EventStream, NodeValueType.Number], [NodeValueType.EventStream], (context, data, inputs, outputs) => {", + " const arr = inputs[0];", + " const count = inputs[1];", + " const result = [];", + " for (let j = 0; j < arr.length; j++) {", + " for (let i = 0; i < count; i++) {", + " result.push(arr[j]);", + " }", + " }", + " outputs[0] = result;", + "});", + "NodeTypes['repeater'] = repeaterNode;", + "const timeNode = new NodeType('time');", + "timeNode.addSignature([], [NodeValueType.Number], (context, data, inputs, outputs) => {", + " outputs[0] = context.emissionState.time;", + "});", + "NodeTypes['time'] = timeNode;", + "const deltaNode = new NodeType('delta');", + "deltaNode.addSignature([], [NodeValueType.Number], (context, data, inputs, outputs) => {", + " outputs[0] = context.delta;", + "});", + "NodeTypes['delta'] = deltaNode;", + "const outputNode = new NodeType('output');", + "outputNode.addSignature([NodeValueType.Number], [NodeValueType.Number], (context, data, inputs, outputs) => {", + " outputs[0] = inputs[0];", + "});", + "outputNode.addSignature([NodeValueType.Vec2], [NodeValueType.Vec2], (context, data, inputs, outputs) => {", + " outputs[0] = inputs[0];", + "});", + "outputNode.addSignature([NodeValueType.Vec3], [NodeValueType.Vec3], (context, data, inputs, outputs) => {", + " outputs[0] = inputs[0];", + "});", + "outputNode.addSignature([NodeValueType.Vec4], [NodeValueType.Vec4], (context, data, inputs, outputs) => {", + " outputs[0] = inputs[0];", + "});", + "outputNode.addSignature([NodeValueType.Boolean], [NodeValueType.Boolean], (context, data, inputs, outputs) => {", + " outputs[0] = inputs[0];", + "});", + "NodeTypes['output'] = outputNode;", + "const lerpNode = new NodeType('lerp');", + "lerpNode.addSignature([NodeValueType.Number, NodeValueType.Number, NodeValueType.Number], [NodeValueType.Number], (context, data, inputs, outputs) => {", + " outputs[0] =", + " inputs[0] * (1 - inputs[2]) + inputs[1] * inputs[2];", + "});", + "lerpNode.addSignature([NodeValueType.Vec2, NodeValueType.Vec2, NodeValueType.Number], [NodeValueType.Vec2], (context, data, inputs, outputs) => {", + " outputs[0].lerpVectors(inputs[0], inputs[1], inputs[2]);", + "});", + "lerpNode.addSignature([NodeValueType.Vec3, NodeValueType.Vec3, NodeValueType.Number], [NodeValueType.Vec3], (context, data, inputs, outputs) => {", + " outputs[0].lerpVectors(inputs[0], inputs[1], inputs[2]);", + "});", + "lerpNode.addSignature([NodeValueType.Vec4, NodeValueType.Vec4, NodeValueType.Number], [NodeValueType.Vec4], (context, data, inputs, outputs) => {", + " outputs[0].lerpVectors(inputs[0], inputs[1], inputs[2]);", + "});", + "NodeTypes['lerp'] = lerpNode;", + "const normalD = (x) => {", + " return (1 / Math.sqrt(2 * Math.PI)) * Math.exp(x * x * -0.5);", + "};", + "const normalDistributionNode = new NodeType('normDistrib');", + "normalDistributionNode.addSignature([NodeValueType.Number], [NodeValueType.Number], (context, data, inputs, outputs) => {", + " outputs[0] = normalD(inputs[0]);", + "});", + "NodeTypes['normDistrib'] = normalDistributionNode;", + "const normcdfNode = new NodeType('normcdf');", + "normcdfNode.addSignature([NodeValueType.Number], [NodeValueType.Number], (context, data, inputs, outputs) => {", + " let x = inputs[0];", + " const a1 = 0.254829592;", + " const a2 = -0.284496736;", + " const a3 = 1.421413741;", + " const a4 = -1.453152027;", + " const a5 = 1.061405429;", + " const p = 0.3275911;", + " let sign = 1;", + " if (x < 0)", + " sign = -1;", + " x = Math.abs(x) / Math.sqrt(2.0);", + " const t = 1.0 / (1.0 + p * x);", + " const y = 1.0 - ((((a5 * t + a4) * t + a3) * t + a2) * t + a1) * t * Math.exp(-x * x);", + " outputs[0] = 0.5 * (1.0 + sign * y);", + "});", + "NodeTypes['normcdf'] = normcdfNode;", + "const normcdfInvNode = new NodeType('normcdfInv');", + "const rationalApproximation = (t) => {", + " const c = [2.515517, 0.802853, 0.010328];", + " const d = [1.432788, 0.189269, 0.001308];", + " return t - ((c[2] * t + c[1]) * t + c[0]) / (((d[2] * t + d[1]) * t + d[0]) * t + 1.0);", + "};", + "const normcdfInv = (p) => {", + " if (p < 0.5) {", + " return -rationalApproximation(Math.sqrt(-2.0 * Math.log(p)));", + " }", + " else {", + " return rationalApproximation(Math.sqrt(-2.0 * Math.log(1 - p)));", + " }", + "};", + "normcdfInvNode.addSignature([NodeValueType.Number], [NodeValueType.Number], (context, data, inputs, outputs) => {", + " outputs[0] = normcdfInv(inputs[0]);", + "});", + "NodeTypes['normcdfInv'] = normcdfInvNode;", + "const clampNode = new NodeType('clamp');", + "clampNode.addSignature([NodeValueType.Number, NodeValueType.Number, NodeValueType.Number], [NodeValueType.Number], (context, data, inputs, outputs) => {", + " outputs[0] = Math.max(Math.min(inputs[0], inputs[2]), inputs[1]);", + "});", + "NodeTypes['clamp'] = clampNode;", + "const smoothstepNode = new NodeType('smoothstep');", + "smoothstepNode.addSignature([NodeValueType.Number, NodeValueType.Number, NodeValueType.Number], [NodeValueType.Number], (context, data, inputs, outputs) => {", + " const x = Math.max(Math.min(inputs[0], inputs[2]), inputs[1]);", + " outputs[0] = x * x * (3 - 2 * x);", + "});", + "NodeTypes['smoothstep'] = smoothstepNode;", + "const randomNode = new NodeType('random');", + "randomNode.addSignature([NodeValueType.Number, NodeValueType.Number], [NodeValueType.Number], (context, data, inputs, outputs) => {", + " outputs[0] = Math.random() * (inputs[1] - inputs[0]) + inputs[0];", + "});", + "randomNode.addSignature([NodeValueType.Vec2, NodeValueType.Vec2], [NodeValueType.Vec2], (context, data, inputs, outputs) => {", + " let random = Math.random();", + " outputs[0].lerpVectors(inputs[0], inputs[1], random);", + "});", + "randomNode.addSignature([NodeValueType.Vec3, NodeValueType.Vec3], [NodeValueType.Vec3], (context, data, inputs, outputs) => {", + " let random = Math.random();", + " outputs[0].lerpVectors(inputs[0], inputs[1], random);", + "});", + "randomNode.addSignature([NodeValueType.Vec4, NodeValueType.Vec4], [NodeValueType.Vec4], (context, data, inputs, outputs) => {", + " let random = Math.random();", + " outputs[0].lerpVectors(inputs[0], inputs[1], random);", + "});", + "NodeTypes['random'] = randomNode;", + "const vrandNode = new NodeType('vrand');", + "vrandNode.addSignature([], [NodeValueType.Vec2], (context, data, inputs, outputs) => {", + " let x = normcdfInv(Math.random());", + " let y = normcdfInv(Math.random());", + " const mag = Math.sqrt(x * x + y * y);", + " outputs[0].set(x / mag, y / mag);", + "});", + "vrandNode.addSignature([], [NodeValueType.Vec3], (context, data, inputs, outputs) => {", + " let x = normcdfInv(Math.random());", + " let y = normcdfInv(Math.random());", + " let z = normcdfInv(Math.random());", + " const mag = Math.sqrt(x * x + y * y + z * z);", + " outputs[0].set(x / mag, y / mag, z / mag);", + "});", + "vrandNode.addSignature([], [NodeValueType.Vec4], (context, data, inputs, outputs) => {", + " let x = normcdfInv(Math.random());", + " let y = normcdfInv(Math.random());", + " let z = normcdfInv(Math.random());", + " let w = normcdfInv(Math.random());", + " const mag = Math.sqrt(x * x + y * y + z * z + w * w);", + " outputs[0].set(x / mag, y / mag, z / mag, w / mag);", + "});", + "NodeTypes['vrand'] = vrandNode;", + "const bsdfNode = new NodeType('bsdf');", + "bsdfNode.addSignature([NodeValueType.Vec3, NodeValueType.Vec3, NodeValueType.Vec3, NodeValueType.Number], [], (context, data, inputs, outputs) => { });", + "NodeTypes['bsdf'] = bsdfNode;", + "const OutputNodeTypeNames = new Set(['output', 'particleProperty', 'graphProperty', 'emit']);", + "", + "class NodeGraph {", + " constructor(name) {", + " this.inputNodes = [];", + " this.outputNodes = [];", + " this.allNodes = new Map();", + " this.wires = [];", + " this.compiled = false;", + " this.nodesInOrder = [];", + " this.version = '1.0';", + " this.uuid = THREE.MathUtils.generateUUID();", + " this.name = name;", + " this.revision = 0;", + " }", + " addWire(wire) {", + " this.wires.push(wire);", + " this.revision++;", + " }", + " addNode(node) {", + " this.allNodes.set(node.id, node);", + " if (node.type === NodeTypes['input']) {", + " this.inputNodes.push(node);", + " }", + " else if (OutputNodeTypeNames.has(node.type.name)) {", + " this.outputNodes.push(node);", + " }", + " this.revision++;", + " }", + " getNode(id) {", + " return this.allNodes.get(id);", + " }", + " deleteNode(node) {", + " this.allNodes.delete(node.id);", + " this.revision++;", + " }", + " deleteWire(wire) {", + " let index = wire.input.outputs[wire.inputIndex].indexOf(wire);", + " if (index !== -1) {", + " wire.input.outputs[wire.inputIndex].splice(index, 1);", + " }", + " wire.output.inputs[wire.outputIndex] = undefined;", + " index = this.wires.indexOf(wire);", + " if (index != -1) {", + " this.wires[index] = this.wires[this.wires.length - 1];", + " this.wires.pop();", + " }", + " this.revision++;", + " }", + " toJSON() {", + " throw new Error('not implemented');", + " }", + " clone() {", + " throw new Error('not implemented');", + " }", + "}", + "", + "new THREE.Vector3(0, 0, 1);", + "const tempQ = new THREE.Quaternion();", + "const tempV = new THREE.Vector3();", + "const tempV2 = new THREE.Vector3();", + "const PREWARM_FPS = 60;", + "const DEFAULT_GEOMETRY = new THREE.PlaneGeometry(1, 1, 1, 1);", + "class NodeVFX {", + " set time(time) {", + " this.emissionState.time = time;", + " }", + " get time() {", + " return this.emissionState.time;", + " }", + " get layers() {", + " return this.rendererSettings.layers;", + " }", + " get texture() {", + " return this.rendererSettings.material.map;", + " }", + " set texture(texture) {", + " this.rendererSettings.material.map = texture;", + " this.neededToUpdateRender = true;", + " }", + " get material() {", + " return this.rendererSettings.material;", + " }", + " set material(material) {", + " this.rendererSettings.material = material;", + " this.neededToUpdateRender = true;", + " }", + " get instancingGeometry() {", + " return this.rendererSettings.instancingGeometry;", + " }", + " set instancingGeometry(geometry) {", + " this.restart();", + " this.particles.length = 0;", + " this.rendererSettings.instancingGeometry = geometry;", + " this.neededToUpdateRender = true;", + " }", + " get renderMode() {", + " return this.rendererSettings.renderMode;", + " }", + " set renderMode(renderMode) {", + " if ((this.rendererSettings.renderMode != RenderMode.Trail && renderMode === RenderMode.Trail) ||", + " (this.rendererSettings.renderMode == RenderMode.Trail && renderMode !== RenderMode.Trail)) {", + " this.restart();", + " this.particles.length = 0;", + " }", + " if (this.rendererSettings.renderMode !== renderMode) {", + " switch (renderMode) {", + " case RenderMode.Trail:", + " this.rendererEmitterSettings = {", + " startLength: 30,", + " followLocalOrigin: false,", + " };", + " break;", + " case RenderMode.Mesh:", + " this.rendererEmitterSettings = {", + " geometry: new THREE.PlaneGeometry(1, 1),", + " };", + " break;", + " case RenderMode.BillBoard:", + " case RenderMode.VerticalBillBoard:", + " case RenderMode.HorizontalBillBoard:", + " case RenderMode.StretchedBillBoard:", + " this.rendererEmitterSettings = {};", + " break;", + " }", + " }", + " this.rendererSettings.renderMode = renderMode;", + " this.neededToUpdateRender = true;", + " }", + " get renderOrder() {", + " return this.rendererSettings.renderOrder;", + " }", + " set renderOrder(renderOrder) {", + " this.rendererSettings.renderOrder = renderOrder;", + " this.neededToUpdateRender = true;", + " }", + " get blending() {", + " return this.rendererSettings.material.blending;", + " }", + " set blending(blending) {", + " this.rendererSettings.material.blending = blending;", + " this.neededToUpdateRender = true;", + " }", + " constructor(parameters) {", + " var _a, _b, _c, _d, _e, _f, _g;", + " this.temp = new THREE.Vector3();", + " this.travelDistance = 0;", + " this.normalMatrix = new THREE.Matrix3();", + " this.speedFactor = 0;", + " this.autoDestroy = parameters.autoDestroy === undefined ? false : parameters.autoDestroy;", + " this.duration = (_a = parameters.duration) !== null && _a !== void 0 ? _a : 1;", + " this.looping = parameters.looping === undefined ? true : parameters.looping;", + " this.prewarm = parameters.prewarm === undefined ? false : parameters.prewarm;", + " this.worldSpace = (_b = parameters.worldSpace) !== null && _b !== void 0 ? _b : false;", + " this.rendererEmitterSettings = (_c = parameters.rendererEmitterSettings) !== null && _c !== void 0 ? _c : {};", + " this.emissionGraph = parameters.emissionGraph;", + " this.updateGraph = parameters.updateGraph;", + " this.interpreter = new Interpreter();", + " this.rendererSettings = {", + " instancingGeometry: (_d = parameters.instancingGeometry) !== null && _d !== void 0 ? _d : DEFAULT_GEOMETRY,", + " renderMode: (_e = parameters.renderMode) !== null && _e !== void 0 ? _e : RenderMode.BillBoard,", + " renderOrder: (_f = parameters.renderOrder) !== null && _f !== void 0 ? _f : 0,", + " material: parameters.material,", + " layers: (_g = parameters.layers) !== null && _g !== void 0 ? _g : new THREE.Layers(),", + " uTileCount: 1,", + " vTileCount: 1,", + " };", + " this.neededToUpdateRender = true;", + " this.particles = new Array();", + " this.emitter = new ParticleEmitter(this);", + " this.paused = false;", + " this.particleNum = 0;", + " this.emissionState = {", + " time: 0,", + " };", + " this.emitEnded = false;", + " this.markForDestroy = false;", + " this.prewarmed = false;", + " }", + " pause() {", + " this.paused = true;", + " }", + " play() {", + " this.paused = false;", + " }", + " spawn(emissionState, matrix) {", + " tempQ.setFromRotationMatrix(matrix);", + " const translation = tempV;", + " const quaternion = tempQ;", + " const scale = tempV2;", + " matrix.decompose(translation, quaternion, scale);", + " this.particleNum++;", + " while (this.particles.length < this.particleNum) {", + " this.particles.push(new NodeParticle());", + " }", + " const particle = this.particles[this.particleNum - 1];", + " particle.reset();", + " this.interpreter.run(this.updateGraph, { particle: particle, emissionState: this.emissionState });", + " if (this.rendererSettings.renderMode === RenderMode.Trail &&", + " this.rendererEmitterSettings.followLocalOrigin) {", + " const trail = particle;", + " trail.localPosition = new THREE.Vector3().copy(trail.position);", + " }", + " if (this.worldSpace) {", + " particle.position.applyMatrix4(matrix);", + " particle.size *= (Math.abs(scale.x) + Math.abs(scale.y) + Math.abs(scale.z)) / 3;", + " particle.velocity.multiply(scale).applyMatrix3(this.normalMatrix);", + " if (particle.rotation && particle.rotation instanceof THREE.Quaternion) {", + " particle.rotation.multiplyQuaternions(tempQ, particle.rotation);", + " }", + " }", + " }", + " endEmit() {", + " this.emitEnded = true;", + " if (this.autoDestroy) {", + " this.markForDestroy = true;", + " }", + " }", + " dispose() {", + " if (this._renderer)", + " this._renderer.deleteSystem(this);", + " this.emitter.dispose();", + " if (this.emitter.parent)", + " this.emitter.parent.remove(this.emitter);", + " }", + " restart() {", + " this.paused = false;", + " this.particleNum = 0;", + " this.emissionState.time = 0;", + " this.emitEnded = false;", + " this.markForDestroy = false;", + " this.prewarmed = false;", + " }", + " update(delta) {", + " if (this.paused)", + " return;", + " let currentParent = this.emitter;", + " while (currentParent.parent) {", + " currentParent = currentParent.parent;", + " }", + " if (currentParent.type !== 'Scene') {", + " this.dispose();", + " return;", + " }", + " if (this.emitEnded && this.particleNum === 0) {", + " if (this.markForDestroy && this.emitter.parent)", + " this.dispose();", + " return;", + " }", + " if (this.looping && this.prewarm && !this.prewarmed) {", + " this.prewarmed = true;", + " for (let i = 0; i < this.duration * PREWARM_FPS; i++) {", + " this.update(1.0 / PREWARM_FPS);", + " }", + " }", + " if (delta > 0.1) {", + " delta = 0.1;", + " }", + " if (this.neededToUpdateRender) {", + " if (this._renderer)", + " this._renderer.updateSystem(this);", + " this.neededToUpdateRender = false;", + " }", + " this.emit(delta, this.emissionState, this.emitter.matrixWorld);", + " const context = { particle: undefined, emissionState: this.emissionState, delta };", + " for (let i = 0; i < this.particleNum; i++) {", + " context.particle = this.particles[i];", + " this.interpreter.run(this.updateGraph, context);", + " }", + " for (let i = 0; i < this.particleNum; i++) {", + " if (this.rendererEmitterSettings.followLocalOrigin &&", + " this.particles[i].localPosition) {", + " this.particles[i].position.copy(this.particles[i].localPosition);", + " if (this.particles[i].parentMatrix) {", + " this.particles[i].position.applyMatrix4(this.particles[i].parentMatrix);", + " }", + " else {", + " this.particles[i].position.applyMatrix4(this.emitter.matrixWorld);", + " }", + " }", + " else {", + " this.particles[i].position.addScaledVector(this.particles[i].velocity, delta);", + " }", + " this.particles[i].age += delta;", + " }", + " if (this.rendererSettings.renderMode === RenderMode.Trail) {", + " for (let i = 0; i < this.particleNum; i++) {", + " const particle = this.particles[i];", + " particle.update();", + " }", + " }", + " for (let i = 0; i < this.particleNum; i++) {", + " const particle = this.particles[i];", + " if (particle.died && (!(particle instanceof TrailParticle) || particle.previous.length === 0)) {", + " this.particles[i] = this.particles[this.particleNum - 1];", + " this.particles[this.particleNum - 1] = particle;", + " this.particleNum--;", + " i--;", + " }", + " }", + " }", + " emit(delta, emissionState, emitterMatrix) {", + " if (emissionState.time > this.duration) {", + " if (this.looping) {", + " emissionState.time -= this.duration;", + " }", + " else {", + " if (!this.emitEnded) {", + " this.endEmit();", + " }", + " }", + " }", + " this.normalMatrix.getNormalMatrix(emitterMatrix);", + " const context = {", + " signal: () => {", + " this.spawn(emissionState, emitterMatrix);", + " },", + " emissionState,", + " delta,", + " };", + " if (!this.emitEnded) {", + " this.interpreter.run(this.emissionGraph, context);", + " }", + " if (this.previousWorldPos === undefined)", + " this.previousWorldPos = new THREE.Vector3();", + " this.emitter.getWorldPosition(this.previousWorldPos);", + " emissionState.time += delta;", + " }", + " toJSON(meta, options = {}) {", + " return {};", + " }", + " getRendererSettings() {", + " return this.rendererSettings;", + " }", + " clone() {", + " return this;", + " }", + "}", + "", + "gdjs.__particleEmmiter3DExtension = {", + " ParticleEmitter3DRenderer,", + " ParticleEmitterAdapter,", + " ParticleEmitterHelper,", + "", + " ApplyCollision,", + " ApplyForce,", + " ApplySequences,", + " AxisAngleGenerator,", + " BatchedParticleRenderer,", + " BatchedRenderer,", + " BehaviorFromJSON,", + " BehaviorTypes,", + " Bezier,", + " ChangeEmitDirection,", + " ColorGeneratorFromJSON,", + " ColorOverLife,", + " ColorRange,", + " ConeEmitter,", + " ConstantColor,", + " ConstantValue,", + " DonutEmitter,", + " EmitSubParticleSystem,", + " EmitterFromJSON,", + " EmitterMode,", + " EmitterShapes,", + " EulerGenerator,", + " ForceOverLife,", + " FrameOverLife,", + " GeneratorFromJSON,", + " Gradient,", + " GraphNodeType,", + " GravityForce,", + " GridEmitter,", + " Interpreter,", + " IntervalValue,", + " MeshSurfaceEmitter,", + " Node,", + " NodeGraph,", + " NodeType,", + " NodeTypes,", + " NodeValueType,", + " Noise,", + " OrbitOverLife,", + " ParticleEmitter,", + " ParticleSystem,", + " PiecewiseBezier,", + " PiecewiseFunction,", + " Plugins,", + " PointEmitter,", + " QuarksLoader,", + " RandomColor,", + " RandomColorBetweenGradient,", + " RandomQuatGenerator,", + " RecordState,", + " RenderMode,", + " Rotation3DOverLife,", + " RotationGeneratorFromJSON,", + " RotationOverLife,", + " SequencerFromJSON,", + " SizeOverLife,", + " SpeedOverLife,", + " SphereEmitter,", + " SpriteBatch,", + " SpriteParticle,", + " SubParticleEmitMode,", + " TextureSequencer,", + " TrailBatch,", + " TrailParticle,", + " TurbulenceField,", + " VFXBatch,", + " ValueGeneratorFromJSON,", + " WidthOverLength,", + " Wire,", + " genDefaultForNodeValueType,", + " getPhysicsResolver,", + " loadPlugin,", + " setPhysicsResolver,", + " unloadPlugin,", + "}" + ], + "parameterObjects": "", + "useStrict": true, + "eventsSheetExpanded": true + } + ], + "parameters": [], + "objectGroups": [] + }, + { + "fullName": "", + "functionType": "Action", + "name": "onScenePreEvents", + "sentence": "", + "events": [ + { + "type": "BuiltinCommonInstructions::JsCode", + "inlineCode": [ + "\r", + "// See doStepPostEvents\r", + "runtimeScene.__particleEmmiter3DExtension = runtimeScene.__particleEmmiter3DExtension || {};\r", + "runtimeScene.__particleEmmiter3DExtension.emittersStepped = 0;" + ], + "parameterObjects": "", + "useStrict": true, + "eventsSheetExpanded": true + } + ], + "parameters": [], + "objectGroups": [] + } + ], + "eventsFunctionsFolderStructure": { + "folderName": "__ROOT", + "children": [ + { + "functionName": "DefineHelperClasses" + }, + { + "functionName": "onScenePreEvents" + } + ] + }, + "eventsBasedBehaviors": [], + "eventsBasedObjects": [ + { + "areaMaxX": 64, + "areaMaxY": 64, + "areaMaxZ": 64, + "areaMinX": 0, + "areaMinY": 0, + "areaMinZ": 0, + "assetStoreTag": "3d particles", + "defaultName": "ParticleEmitter", + "description": "Display a large number of particles to create visual effects.", + "fullName": "3D particle emitter", + "helpPath": "", + "iconUrl": "", + "is3D": true, + "isUsingLegacyInstancesRenderer": true, + "name": "ParticleEmitter3D", + "previewIconUrl": "", + "objects": [ + { + "adaptCollisionMaskAutomatically": true, + "assetStoreId": "", + "name": "Particle", + "type": "Sprite", + "updateIfNotVisible": false, + "variables": [], + "effects": [], + "behaviors": [], + "animations": [ + { + "name": "Image", + "useMultipleDirections": false, + "directions": [ + { + "looping": false, + "timeBetweenFrames": 0.08, + "sprites": [] + } + ] + } + ] + } + ], + "objectsFolderStructure": { + "folderName": "__ROOT", + "children": [ + { + "objectName": "Particle" + } + ] + }, + "objectsGroups": [], + "layers": [ + { + "ambientLightColorB": 200, + "ambientLightColorG": 200, + "ambientLightColorR": 200, + "camera2DPlaneMaxDrawingDistance": 5000, + "camera3DFarPlaneDistance": 10000, + "camera3DFieldOfView": 45, + "camera3DNearPlaneDistance": 3, + "cameraType": "", + "followBaseLayerCamera": false, + "isLightingLayer": false, + "isLocked": false, + "name": "", + "renderingType": "", + "visibility": true, + "cameras": [ + { + "defaultSize": true, + "defaultViewport": true, + "height": 0, + "viewportBottom": 1, + "viewportLeft": 0, + "viewportRight": 1, + "viewportTop": 0, + "width": 0 + } + ], + "effects": [] + } + ], + "instances": [], + "editionSettings": [], + "eventsFunctions": [ + { + "fullName": "", + "functionType": "Action", + "name": "onCreated", + "sentence": "", + "events": [ + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [], + "actions": [ + { + "type": { + "value": "ParticleEmitter3D::DefineHelperClasses" + }, + "parameters": [ + "", + "" + ] + } + ] + }, + { + "type": "BuiltinCommonInstructions::JsCode", + "inlineCode": [ + "const {", + " ParticleEmitterAdapter,", + " ParticleEmitter3DRenderer,", + " ParticleEmitterHelper,", + "", + " ParticleSystem,", + " IntervalValue,", + " ConstantValue,", + " ConstantColor,", + " ColorOverLife,", + " SizeOverLife,", + " ApplyForce,", + " WidthOverLength,", + " Gradient,", + " PiecewiseBezier,", + " Bezier,", + " PointEmitter,", + " ConeEmitter,", + " RenderMode", + "} = gdjs.__particleEmmiter3DExtension;", + "", + "/** @type {gdjs.CustomRuntimeObject} */", + "const object = objects[0];", + "// Force the size of the object because it doesn't contain any instance.", + "object._innerArea = {", + " min: [-16, -16, -16],", + " max: [16, 16, 16],", + " };", + "const hitbox = new gdjs.Polygon();", + "hitbox.vertices = [[-16, -16], [16, -16], [16, 16], [-16, 16]];", + "object._untransformedHitBoxes = [hitbox];", + "object._unrotatedAABB.min[0] = -16;", + "object._unrotatedAABB.min[1] = -16;", + "object._unrotatedAABB.max[0] = 16;", + "object._unrotatedAABB.max[1] = 16;", + "object._updateUntransformedHitBoxes = () => {};", + "", + "// Here runtimeScene is the gdjs.CustomRuntimeObjectInstanceContainer inside the custom object.", + "const gameScene = object.getRuntimeScene();", + "const game = runtimeScene.getGame();", + "", + "const texture = game.getImageManager().getThreeTexture('');", + "", + "// Set the blending here because changes are not applied after the emitter creation.", + "const blendingString = object._getBlending();", + "const blending =", + " blendingString === \"Additive\" ? THREE.AdditiveBlending :", + " blendingString === \"Normal\" ? THREE.NormalBlending :", + " blendingString === \"Subtractive\" ? THREE.SubtractiveBlending :", + " blendingString === \"Multiply\" ? THREE.MultiplyBlending :", + " blendingString === \"None\" ? THREE.NoBlending :", + " THREE.AdditiveBlending;", + "", + "", + "// Build a configuration with the right kind of objects.", + "// These values are not important as they are overrided by the object properties values.", + "const muzzle = {", + " duration: 10,", + " looping: false,", + " startLife: new IntervalValue(1, 2),", + " startSpeed: new IntervalValue(100, 200),", + " startSize: new IntervalValue(20, 50),", + " startColor: new ConstantColor(new THREE.Vector4(1, 1, 1, 1)),", + " worldSpace: true,", + "", + " maxParticle: 5,", + " emissionOverTime: new ConstantValue(50),", + " emissionBursts: [{", + " time: 0,", + " count: new ConstantValue(1),", + " cycle: 1,", + " interval: 0.01,", + " probability: 1,", + " }],", + "", + " shape: new PointEmitter(),", + " material: new THREE.MeshBasicMaterial({", + " map: texture,", + " blending: blending,", + " transparent: true,", + " side: THREE.DoubleSide", + " }),", + " startTileIndex: 0,", + " uTileCount: 1,", + " vTileCount: 1,", + " renderOrder: 2,", + " renderMode: RenderMode.BillBoard", + "};", + "const particleSystem = new ParticleSystem(muzzle);", + "", + "const colorOverLife = new ColorOverLife(", + " new Gradient(", + " [", + " [new THREE.Vector3(1, 0, 0), 0],", + " [new THREE.Vector3(1, 0, 0), 1],", + " ],", + " [", + " [1, 0],", + " [1, 1],", + " ]", + " ));", + "particleSystem.addBehavior(colorOverLife);", + "const sizeOverLife = new SizeOverLife(new PiecewiseBezier([[new Bezier(1, 2 / 3, 1 / 3, 0), 0]]));", + "particleSystem.addBehavior(sizeOverLife);", + "const applyForce = new ApplyForce(new THREE.Vector3(0, 0, -1), new ConstantValue(0));", + "particleSystem.addBehavior(applyForce);", + "const widthOverLength = new WidthOverLength(new PiecewiseBezier([[new Bezier(1, 2 / 3, 1 / 3, 0), 0]]));", + "particleSystem.addBehavior(widthOverLength);", + "", + "particleSystem.emitter.name = object.getName() + object.getNameId();", + "", + "object.__particleEmitterAdapter = new ParticleEmitterAdapter(particleSystem, colorOverLife, sizeOverLife, applyForce, widthOverLength);", + "object.__particleSystem = particleSystem;", + "", + "// This is a hack that may break in future releases.", + "// Replace the group that would hold children objects by the emmiter.", + "const layer = object.getInstanceContainer().getLayer(object.getLayer());", + "const group = object.getRenderer()._threeGroup;", + "layer.getRenderer().remove3DRendererObject(group);", + "particleSystem.emitter.position.copy(group.position);", + "particleSystem.emitter.rotation.order = 'ZYX';", + "particleSystem.emitter.rotation.copy(group.rotation);", + "", + "const particleEmitter3DRenderer = new ParticleEmitter3DRenderer(object, object._instanceContainer, object.getInstanceContainer());", + "object._renderer = particleEmitter3DRenderer;", + "if (game.isInGameEdition && game.isInGameEdition()) {", + " const particleEmitterHelper = new ParticleEmitterHelper(object);", + " particleEmitter3DRenderer._threeGroup = particleEmitterHelper;", + " layer.getRenderer().add3DRendererObject(particleEmitterHelper);", + "}", + "else {", + " particleEmitter3DRenderer._threeGroup = particleSystem.emitter;", + " layer.getRenderer().add3DRendererObject(particleSystem.emitter);", + "}", + "", + "particleSystem.emitter.updateMatrixWorld(true);", + "", + "", + "// See doStepPostEvents", + "gameScene.__particleEmmiter3DExtension = gameScene.__particleEmmiter3DExtension || {};", + "gameScene.__particleEmmiter3DExtension.emittersCount = gameScene.__particleEmmiter3DExtension.emittersCount || 0;", + "gameScene.__particleEmmiter3DExtension.emittersCount++;", + "", + "" + ], + "parameterObjects": "Object", + "useStrict": true, + "eventsSheetExpanded": true + }, + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [], + "actions": [ + { + "type": { + "value": "ParticleEmitter3D::ParticleEmitter3D::UpdateFromProperties" + }, + "parameters": [ + "Object", + "" + ] + }, + { + "type": { + "value": "ParticleEmitter3D::ParticleEmitter3D::SetRotationCenter" + }, + "parameters": [ + "Object", + "0", + "0", + "0" + ] + } + ] + } + ], + "parameters": [ + { + "description": "Object", + "name": "Object", + "supplementaryInformation": "ParticleEmitter3D::ParticleEmitter3D", + "type": "object" + } + ], + "objectGroups": [] + }, + { + "fullName": "", + "functionType": "Action", + "name": "onDestroy", + "sentence": "", + "events": [ + { + "type": "BuiltinCommonInstructions::JsCode", + "inlineCode": [ + "/** @type {gdjs.CustomRuntimeObject} */", + "const object = objects[0];", + "// Here runtimeScene is the gdjs.CustomRuntimeObjectInstanceContainer inside the custom object.", + "const gameScene = object.getRuntimeScene();", + "", + "object.__particleSystem.dispose();", + "", + "// See doStepPostEvents", + "gameScene.__particleEmmiter3DExtension.emittersCount--;", + "", + "if (gameScene.__particleEmmiter3DExtension.emittersCount === 0) {", + " // Update batch system now because doStepPostEvents won't be called.", + " gameScene.__particleEmmiter3DExtension.layerNames = gameScene.__particleEmmiter3DExtension.layerNames || [];", + " const layerNames = gameScene.__particleEmmiter3DExtension.layerNames;", + " gameScene.getAllLayerNames(layerNames);", + " for (const layerName of layerNames) {", + " const layer = gameScene.getLayer(layerName);", + " if (layer.__particleEmmiter3DExtension) {", + " layer.__particleEmmiter3DExtension.batchSystem.update(layer.getElapsedTime() / 1000);", + " }", + " }", + "}" + ], + "parameterObjects": "Object", + "useStrict": true, + "eventsSheetExpanded": true + } + ], + "parameters": [ + { + "description": "Object", + "name": "Object", + "supplementaryInformation": "ParticleEmitter3D::ParticleEmitter3D", + "type": "object" + } + ], + "objectGroups": [] + }, + { + "fullName": "", + "functionType": "Action", + "name": "onHotReloading", + "sentence": "", + "events": [ + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [], + "actions": [ + { + "type": { + "value": "ParticleEmitter3D::ParticleEmitter3D::UpdateFromProperties" + }, + "parameters": [ + "Object", + "" + ] + } + ] + } + ], + "parameters": [ + { + "description": "Object", + "name": "Object", + "supplementaryInformation": "ParticleEmitter3D::ParticleEmitter3D", + "type": "object" + } + ], + "objectGroups": [] + }, + { + "description": "Update from properties.", + "fullName": "Update from properties", + "functionType": "Action", + "name": "UpdateFromProperties", + "private": true, + "sentence": "Update from properties of _PARAM0_", + "events": [ + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [], + "actions": [ + { + "type": { + "value": "ParticleEmitter3D::ParticleEmitter3D::SetZ" + }, + "parameters": [ + "Object", + "=", + "Object.Z()", + "" + ] + }, + { + "type": { + "value": "ParticleEmitter3D::ParticleEmitter3D::SetRotationX" + }, + "parameters": [ + "Object", + "=", + "Object.PropertyRotationX()", + "" + ] + }, + { + "type": { + "value": "ParticleEmitter3D::ParticleEmitter3D::SetRotationY" + }, + "parameters": [ + "Object", + "=", + "Object.PropertyRotationY()", + "" + ] + }, + { + "type": { + "value": "ParticleEmitter3D::ParticleEmitter3D::SetRenderMode" + }, + "parameters": [ + "Object", + "=", + "RenderMode", + "" + ] + }, + { + "type": { + "value": "ParticleEmitter3D::ParticleEmitter3D::SetTrailStartLengthMin" + }, + "parameters": [ + "Object", + "=", + "TrailStartLengthMin", + "" + ] + }, + { + "type": { + "value": "ParticleEmitter3D::ParticleEmitter3D::SetTrailStartLengthMax" + }, + "parameters": [ + "Object", + "=", + "TrailStartLengthMax", + "" + ] + }, + { + "type": { + "value": "ParticleEmitter3D::ParticleEmitter3D::SetTrailEndWidthRatio" + }, + "parameters": [ + "Object", + "=", + "TrailEndWidthRatio", + "" + ] + }, + { + "type": { + "value": "ParticleEmitter3D::ParticleEmitter3D::SetStartColor" + }, + "parameters": [ + "Object", + "=", + "Object.StartColor()", + "" + ] + }, + { + "type": { + "value": "ParticleEmitter3D::ParticleEmitter3D::SetEndColor" + }, + "parameters": [ + "Object", + "=", + "Object.EndColor()", + "" + ] + }, + { + "type": { + "value": "ParticleEmitter3D::ParticleEmitter3D::SetStartOpacity" + }, + "parameters": [ + "Object", + "=", + "Object.StartOpacity()", + "" + ] + }, + { + "type": { + "value": "ParticleEmitter3D::ParticleEmitter3D::SetEndOpacity" + }, + "parameters": [ + "Object", + "=", + "Object.EndOpacity()", + "" + ] + }, + { + "type": { + "value": "ParticleEmitter3D::ParticleEmitter3D::SetFlow" + }, + "parameters": [ + "Object", + "=", + "Object.Flow()", + "" + ] + }, + { + "type": { + "value": "ParticleEmitter3D::ParticleEmitter3D::SetStartSizeMin" + }, + "parameters": [ + "Object", + "=", + "Object.StartSizeMin()", + "" + ] + }, + { + "type": { + "value": "ParticleEmitter3D::ParticleEmitter3D::SetStartSizeMax" + }, + "parameters": [ + "Object", + "=", + "Object.StartSizeMax()", + "" + ] + }, + { + "type": { + "value": "ParticleEmitter3D::ParticleEmitter3D::SetEndScale" + }, + "parameters": [ + "Object", + "=", + "Object.EndScale()", + "" + ] + }, + { + "type": { + "value": "ParticleEmitter3D::ParticleEmitter3D::SetStartSpeedMin" + }, + "parameters": [ + "Object", + "=", + "Object.StartSpeedMin()", + "" + ] + }, + { + "type": { + "value": "ParticleEmitter3D::ParticleEmitter3D::SetStartSpeedMax" + }, + "parameters": [ + "Object", + "=", + "Object.StartSpeedMax()", + "" + ] + }, + { + "type": { + "value": "ParticleEmitter3D::ParticleEmitter3D::SetLifespanMin" + }, + "parameters": [ + "Object", + "=", + "Object.LifespanMin()", + "" + ] + }, + { + "type": { + "value": "ParticleEmitter3D::ParticleEmitter3D::SetLifespanMax" + }, + "parameters": [ + "Object", + "=", + "Object.LifespanMax()", + "" + ] + }, + { + "type": { + "value": "ParticleEmitter3D::ParticleEmitter3D::SetDuration" + }, + "parameters": [ + "Object", + "=", + "Object.Duration()", + "" + ] + }, + { + "type": { + "value": "ParticleEmitter3D::ParticleEmitter3D::SetSpayConeAngle" + }, + "parameters": [ + "Object", + "=", + "Object.SpayConeAngle()", + "" + ] + }, + { + "type": { + "value": "ParticleEmitter3D::ParticleEmitter3D::SetGravity" + }, + "parameters": [ + "Object", + "=", + "Object.Gravity()", + "" + ] + }, + { + "type": { + "value": "ParticleEmitter3D::ParticleEmitter3D::SetGravityTop" + }, + "parameters": [ + "Object", + "=", + "Object.GravityTop()", + "" + ] + }, + { + "type": { + "value": "ParticleEmitter3D::ParticleEmitter3D::UpdateImage" + }, + "parameters": [ + "Object", + "" + ] + } + ], + "events": [ + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [ + { + "type": { + "inverted": true, + "value": "BooleanVariable" + }, + "parameters": [ + "IsTrailFollowingLocalOrigin", + "True", + "" + ] + } + ], + "actions": [ + { + "type": { + "value": "ParticleEmitter3D::ParticleEmitter3D::SetIsTrailFollowingLocalOrigin" + }, + "parameters": [ + "Object", + "=", + "TrailStartLengthMax" + ] + } + ] + }, + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [ + { + "type": { + "value": "BooleanVariable" + }, + "parameters": [ + "IsTrailFollowingLocalOrigin", + "True", + "" + ] + } + ], + "actions": [ + { + "type": { + "value": "ParticleEmitter3D::ParticleEmitter3D::SetIsTrailFollowingLocalOrigin" + }, + "parameters": [ + "Object", + "yes", + "TrailStartLengthMax" + ] + } + ] + }, + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [ + { + "type": { + "inverted": true, + "value": "BooleanVariable" + }, + "parameters": [ + "AreParticlesRelative", + "True", + "" + ] + } + ], + "actions": [ + { + "type": { + "value": "ParticleEmitter3D::ParticleEmitter3D::SetAreParticlesRelative" + }, + "parameters": [ + "Object", + "no", + "" + ] + } + ] + }, + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [ + { + "type": { + "value": "BooleanVariable" + }, + "parameters": [ + "AreParticlesRelative", + "True", + "" + ] + } + ], + "actions": [ + { + "type": { + "value": "ParticleEmitter3D::ParticleEmitter3D::SetAreParticlesRelative" + }, + "parameters": [ + "Object", + "yes", + "" + ] + } + ] + }, + { + "type": "BuiltinCommonInstructions::Comment", + "color": { + "b": 109, + "g": 230, + "r": 255, + "textB": 0, + "textG": 0, + "textR": 0 + }, + "comment": "TODO: Blending can't be changed." + }, + { + "disabled": true, + "type": "BuiltinCommonInstructions::Standard", + "conditions": [], + "actions": [ + { + "type": { + "value": "ParticleEmitter3D::ParticleEmitter3D::SetBlending" + }, + "parameters": [ + "Object", + "=", + "Object.Blending()", + "" + ] + } + ] + } + ] + }, + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [], + "actions": [ + { + "type": { + "value": "ParticleEmitter3D::ParticleEmitter3D::UpdateHelper" + }, + "parameters": [ + "Object", + "" + ] + } + ] + } + ], + "parameters": [ + { + "description": "Object", + "name": "Object", + "supplementaryInformation": "ParticleEmitter3D::ParticleEmitter3D", + "type": "object" + } + ], + "objectGroups": [] + }, + { + "fullName": "Update helper", + "functionType": "Action", + "name": "UpdateHelper", + "private": true, + "sentence": "Update graphical helper of _PARAM0_", + "events": [ + { + "type": "BuiltinCommonInstructions::JsCode", + "inlineCode": [ + "const game = runtimeScene.getGame();", + "if (game.isInGameEdition && game.isInGameEdition()) {", + " const particleEmitterHelper = objects[0].get3DRendererObject();", + " particleEmitterHelper.update();", + "}" + ], + "parameterObjects": "Object", + "useStrict": true, + "eventsSheetExpanded": false + } + ], + "parameters": [ + { + "description": "Object", + "name": "Object", + "supplementaryInformation": "ParticleEmitter3D::ParticleEmitter3D", + "type": "object" + } + ], + "objectGroups": [] + }, + { + "fullName": "Update particle image", + "functionType": "Action", + "name": "UpdateImage", + "private": true, + "sentence": "Update particle image of _PARAM0_", + "events": [ + { + "type": "BuiltinCommonInstructions::JsCode", + "inlineCode": [ + "/** @type {gdjs.CustomRuntimeObject3D} */\r", + "const object = objects[0];\r", + "/** @type {SpriteObjectDataType} */\r", + "const particleSpriteData = object._instanceContainer._objects.get(\"Particle\");\r", + "/** @type {gdjs.SpriteAnimationData} */\r", + "const animation = particleSpriteData.animations[0];\r", + "const animationFrame = animation ? animation.directions[0].sprites[0] : null;\r", + "const resourceName = animationFrame ? animationFrame.image : '';\r", + "object.__particleEmitterAdapter.setImage(resourceName);" + ], + "parameterObjects": "Object", + "useStrict": true, + "eventsSheetExpanded": true + } + ], + "parameters": [ + { + "description": "Object", + "name": "Object", + "supplementaryInformation": "ParticleEmitter3D::ParticleEmitter3D", + "type": "object" + } + ], + "objectGroups": [] + }, + { + "fullName": "", + "functionType": "Action", + "name": "doStepPostEvents", + "sentence": "", + "events": [ + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [ + { + "type": { + "value": "ParticleEmitter3D::ParticleEmitter3D::HasEnded" + }, + "parameters": [ + "Object", + "" + ] + } + ], + "actions": [ + { + "type": { + "value": "ParticleEmitter3D::ParticleEmitter3D::Delete" + }, + "parameters": [ + "Object", + "" + ] + } + ] + }, + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [ + { + "type": { + "inverted": true, + "value": "ParticleEmitter3D::ParticleEmitter3D::HasEnded" + }, + "parameters": [ + "Object", + "" + ] + } + ], + "actions": [ + { + "type": { + "value": "ParticleEmitter3D::ParticleEmitter3D::RegisterInLayer" + }, + "parameters": [ + "Object", + "" + ] + } + ], + "events": [ + { + "type": "BuiltinCommonInstructions::JsCode", + "inlineCode": [ + "/** @type {gdjs.CustomRuntimeObject3D} */", + "const object = objects[0];", + "// Here runtimeScene is the gdjs.CustomRuntimeObjectInstanceContainer inside the custom object.", + "const gameScene = object.getRuntimeScene();", + "", + "// Update batch system after having moved every emitter.", + "// See onScenePreEvents", + "gameScene.__particleEmmiter3DExtension.emittersStepped++;", + "if (gameScene.__particleEmmiter3DExtension.emittersStepped === gameScene.__particleEmmiter3DExtension.emittersCount) {", + " gameScene.__particleEmmiter3DExtension.layerNames = gameScene.__particleEmmiter3DExtension.layerNames || [];", + " const layerNames = gameScene.__particleEmmiter3DExtension.layerNames;", + " gameScene.getAllLayerNames(layerNames);", + " for (const layerName of layerNames) {", + " const layer = gameScene.getLayer(layerName);", + " if (layer.__particleEmmiter3DExtension) {", + " const timeDelta = layer.getElapsedTime() / 1000;", + " if (timeDelta > 0) {", + " layer.__particleEmmiter3DExtension.batchSystem.update(timeDelta);", + " }", + " }", + " }", + "}" + ], + "parameterObjects": "Object", + "useStrict": true, + "eventsSheetExpanded": true + } + ] + } + ], + "parameters": [ + { + "description": "Object", + "name": "Object", + "supplementaryInformation": "ParticleEmitter3D::ParticleEmitter3D", + "type": "object" + } + ], + "objectGroups": [] + }, + { + "fullName": "Register in layer", + "functionType": "Action", + "name": "RegisterInLayer", + "private": true, + "sentence": "Register _PARAM0_ in layer", + "events": [ + { + "type": "BuiltinCommonInstructions::JsCode", + "inlineCode": [ + "/** @type {gdjs.CustomRuntimeObject} */", + "const object = objects[0];", + "", + "// TODO Handle layer changes?", + "if (object.__emitterLayerName === undefined) {", + " // Here runtimeScene is the gdjs.CustomRuntimeObjectInstanceContainer inside the custom object.", + " const gameScene = object.getRuntimeScene();", + "", + " let rootObject = object;", + " while (rootObject.getInstanceContainer().getOwner) {", + " rootObject = rootObject.getInstanceContainer().getOwner();", + " }", + "", + " const layer = gameScene.getLayer(rootObject.getLayer());", + " layer.__particleEmmiter3DExtension = layer.__particleEmmiter3DExtension || {};", + " if (!layer.__particleEmmiter3DExtension.batchSystem) {", + " const batchSystem = new gdjs.__particleEmmiter3DExtension.BatchedRenderer();", + " batchSystem.scale.y *= -1;", + " const threeScene = layer.getRenderer().getThreeScene();", + " if (threeScene) {", + " threeScene.add(batchSystem);", + " }", + " layer.__particleEmmiter3DExtension.batchSystem = batchSystem;", + " }", + " layer.__particleEmmiter3DExtension.batchSystem.addSystem(object.__particleSystem);", + " object.__emitterLayerName = layer.getName();", + "}" + ], + "parameterObjects": "Object", + "useStrict": true, + "eventsSheetExpanded": true + } + ], + "parameters": [ + { + "description": "Object", + "name": "Object", + "supplementaryInformation": "ParticleEmitter3D::ParticleEmitter3D", + "type": "object" + } + ], + "objectGroups": [] + }, + { + "description": "Delete itself", + "fullName": "Delete itself", + "functionType": "Action", + "name": "Delete", + "private": true, + "sentence": "Delete _PARAM0_", + "events": [ + { + "type": "BuiltinCommonInstructions::JsCode", + "inlineCode": [ + "const object = objects[0];", + "", + "object.deleteFromScene(object.getParent());", + "" + ], + "parameterObjects": "Object", + "useStrict": true, + "eventsSheetExpanded": true + } + ], + "parameters": [ + { + "description": "Object", + "name": "Object", + "supplementaryInformation": "ParticleEmitter3D::ParticleEmitter3D", + "type": "object" + } + ], + "objectGroups": [] + }, + { + "description": "Check that emission has ended and no particle is alive anymore.", + "fullName": "Emission has ended", + "functionType": "Condition", + "name": "HasEnded", + "sentence": "Emission from _PARAM0_ has ended", + "events": [ + { + "type": "BuiltinCommonInstructions::JsCode", + "inlineCode": [ + "const object = objects[0];\r", + "\r", + "eventsFunctionContext.returnValue =\r", + " object._getShouldAutodestruct()\r", + " && object.__particleSystem.emitEnded\r", + " && object.__particleSystem.particleNum === 0;\r", + "" + ], + "parameterObjects": "Object", + "useStrict": true, + "eventsSheetExpanded": true + } + ], + "parameters": [ + { + "description": "Object", + "name": "Object", + "supplementaryInformation": "ParticleEmitter3D::ParticleEmitter3D", + "type": "object" + } + ], + "objectGroups": [] + }, + { + "description": "Restart particule emission from the beginning.", + "fullName": "Restart", + "functionType": "Action", + "name": "Restart", + "sentence": "Restart particule emission from _PARAM0_", + "events": [ + { + "type": "BuiltinCommonInstructions::JsCode", + "inlineCode": [ + "/** @type {gdjs.CustomRuntimeObject} */\r", + "const object = objects[0];\r", + "\r", + "object.__particleSystem.restart();\r", + "" + ], + "parameterObjects": "Object", + "useStrict": true, + "eventsSheetExpanded": false + } + ], + "parameters": [ + { + "description": "Object", + "name": "Object", + "supplementaryInformation": "ParticleEmitter3D::ParticleEmitter3D", + "type": "object" + } + ], + "objectGroups": [] + }, + { + "description": "the Z position of the emitter.", + "fullName": "Z elevaltion (deprecated)", + "functionType": "ExpressionAndCondition", + "group": "Position", + "name": "Z", + "private": true, + "sentence": "the Z position", + "events": [ + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [], + "actions": [ + { + "type": { + "value": "SetReturnNumber" + }, + "parameters": [ + "Object.PropertyZ()" + ] + } + ] + } + ], + "expressionType": { + "type": "expression" + }, + "parameters": [ + { + "description": "Object", + "name": "Object", + "supplementaryInformation": "ParticleEmitter3D::ParticleEmitter3D", + "type": "object" + } + ], + "objectGroups": [] + }, + { + "fullName": "", + "functionType": "ActionWithOperator", + "getterName": "Z", + "group": "Position", + "name": "SetZ", + "private": true, + "sentence": "", + "events": [ + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [], + "actions": [ + { + "type": { + "value": "SetNumberVariable" + }, + "parameters": [ + "Z", + "=", + "Value" + ] + } + ] + } + ], + "parameters": [ + { + "description": "Object", + "name": "Object", + "supplementaryInformation": "ParticleEmitter3D::ParticleEmitter3D", + "type": "object" + } + ], + "objectGroups": [] + }, + { + "description": "the rotation on X axis of the emitter.", + "fullName": "Rotation on X axis (deprecated)", + "functionType": "ExpressionAndCondition", + "group": "Angle", + "name": "RotationX", + "private": true, + "sentence": "the rotation on X axis", + "events": [ + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [], + "actions": [ + { + "type": { + "value": "SetReturnNumber" + }, + "parameters": [ + "Object.PropertyRotationX()" + ] + } + ] + } + ], + "expressionType": { + "type": "expression" + }, + "parameters": [ + { + "description": "Object", + "name": "Object", + "supplementaryInformation": "ParticleEmitter3D::ParticleEmitter3D", + "type": "object" + } + ], + "objectGroups": [] + }, + { + "fullName": "", + "functionType": "ActionWithOperator", + "getterName": "RotationX", + "group": "Angle", + "name": "SetRotationX", + "private": true, + "sentence": "", + "events": [ + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [], + "actions": [ + { + "type": { + "value": "SetNumberVariable" + }, + "parameters": [ + "RotationX", + "=", + "Value" + ] + } + ] + }, + { + "type": "BuiltinCommonInstructions::JsCode", + "inlineCode": [ + "const object = objects[0];\r", + "\r", + "object.__particleSystem.emitter.rotation.x = eventsFunctionContext.getArgument(\"Value\") * Math.PI / 180;" + ], + "parameterObjects": "Object", + "useStrict": true, + "eventsSheetExpanded": false + } + ], + "parameters": [ + { + "description": "Object", + "name": "Object", + "supplementaryInformation": "ParticleEmitter3D::ParticleEmitter3D", + "type": "object" + } + ], + "objectGroups": [] + }, + { + "description": "the rotation on Y axis of the emitter.", + "fullName": "Rotation on Y axis (deprecated)", + "functionType": "ExpressionAndCondition", + "group": "Angle", + "name": "RotationY", + "private": true, + "sentence": "the rotation on Y axis", + "events": [ + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [], + "actions": [ + { + "type": { + "value": "SetReturnNumber" + }, + "parameters": [ + "Object.PropertyRotationY()" + ] + } + ] + } + ], + "expressionType": { + "type": "expression" + }, + "parameters": [ + { + "description": "Object", + "name": "Object", + "supplementaryInformation": "ParticleEmitter3D::ParticleEmitter3D", + "type": "object" + } + ], + "objectGroups": [] + }, + { + "fullName": "", + "functionType": "ActionWithOperator", + "getterName": "RotationY", + "group": "Angle", + "name": "SetRotationY", + "private": true, + "sentence": "", + "events": [ + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [], + "actions": [ + { + "type": { + "value": "SetNumberVariable" + }, + "parameters": [ + "RotationY", + "=", + "Value" + ] + } + ] + }, + { + "type": "BuiltinCommonInstructions::JsCode", + "inlineCode": [ + "const object = objects[0];\r", + "\r", + "object.__particleSystem.emitter.rotation.y = (90 + eventsFunctionContext.getArgument(\"Value\")) * Math.PI / 180;" + ], + "parameterObjects": "Object", + "useStrict": true, + "eventsSheetExpanded": false + } + ], + "parameters": [ + { + "description": "Object", + "name": "Object", + "supplementaryInformation": "ParticleEmitter3D::ParticleEmitter3D", + "type": "object" + } + ], + "objectGroups": [] + }, + { + "description": "the start color of the object.", + "fullName": "Start color", + "functionType": "ExpressionAndCondition", + "group": "3D particle emitter color configuration", + "name": "StartColor", + "sentence": "the start color", + "events": [ + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [], + "actions": [ + { + "type": { + "value": "SetReturnString" + }, + "parameters": [ + "StartColor" + ] + } + ] + } + ], + "expressionType": { + "type": "color" + }, + "parameters": [ + { + "description": "Object", + "name": "Object", + "supplementaryInformation": "ParticleEmitter3D::ParticleEmitter3D", + "type": "object" + } + ], + "objectGroups": [] + }, + { + "fullName": "", + "functionType": "ActionWithOperator", + "getterName": "StartColor", + "group": "3D particle emitter color configuration", + "name": "SetStartColor", + "sentence": "", + "events": [ + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [], + "actions": [ + { + "type": { + "value": "SetStringVariable" + }, + "parameters": [ + "StartColor", + "=", + "Value" + ] + } + ] + }, + { + "type": "BuiltinCommonInstructions::JsCode", + "inlineCode": [ + "const object = objects[0];\r", + "const startColor = eventsFunctionContext.getArgument(\"Value\");\r", + "\r", + "object.__particleEmitterAdapter.setStartColor(startColor);" + ], + "parameterObjects": "Object", + "useStrict": true, + "eventsSheetExpanded": false + } + ], + "parameters": [ + { + "description": "Object", + "name": "Object", + "supplementaryInformation": "ParticleEmitter3D::ParticleEmitter3D", + "type": "object" + } + ], + "objectGroups": [] + }, + { + "description": "the end color of the object.", + "fullName": "End color", + "functionType": "ExpressionAndCondition", + "group": "3D particle emitter color configuration", + "name": "EndColor", + "sentence": "the end color", + "events": [ + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [], + "actions": [ + { + "type": { + "value": "SetReturnString" + }, + "parameters": [ + "EndColor" + ] + } + ] + } + ], + "expressionType": { + "type": "color" + }, + "parameters": [ + { + "description": "Object", + "name": "Object", + "supplementaryInformation": "ParticleEmitter3D::ParticleEmitter3D", + "type": "object" + } + ], + "objectGroups": [] + }, + { + "fullName": "", + "functionType": "ActionWithOperator", + "getterName": "EndColor", + "group": "3D particle emitter color configuration", + "name": "SetEndColor", + "sentence": "", + "events": [ + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [], + "actions": [ + { + "type": { + "value": "SetStringVariable" + }, + "parameters": [ + "EndColor", + "=", + "Value" + ] + } + ] + }, + { + "type": "BuiltinCommonInstructions::JsCode", + "inlineCode": [ + "const object = objects[0];\r", + "const endColor = eventsFunctionContext.getArgument(\"Value\");\r", + "\r", + "object.__particleEmitterAdapter.setEndColor(endColor);" + ], + "parameterObjects": "Object", + "useStrict": true, + "eventsSheetExpanded": false + } + ], + "parameters": [ + { + "description": "Object", + "name": "Object", + "supplementaryInformation": "ParticleEmitter3D::ParticleEmitter3D", + "type": "object" + } + ], + "objectGroups": [] + }, + { + "description": "the start opacity of the object.", + "fullName": "Start opacity", + "functionType": "ExpressionAndCondition", + "group": "3D particle emitter color configuration", + "name": "StartOpacity", + "sentence": "the start opacity", + "events": [ + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [], + "actions": [ + { + "type": { + "value": "SetReturnNumber" + }, + "parameters": [ + "StartOpacity" + ] + } + ] + } + ], + "expressionType": { + "type": "expression" + }, + "parameters": [ + { + "description": "Object", + "name": "Object", + "supplementaryInformation": "ParticleEmitter3D::ParticleEmitter3D", + "type": "object" + } + ], + "objectGroups": [] + }, + { + "fullName": "", + "functionType": "ActionWithOperator", + "getterName": "StartOpacity", + "group": "3D particle emitter color configuration", + "name": "SetStartOpacity", + "sentence": "", + "events": [ + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [], + "actions": [ + { + "type": { + "value": "SetNumberVariable" + }, + "parameters": [ + "StartOpacity", + "=", + "Value" + ] + } + ] + }, + { + "type": "BuiltinCommonInstructions::JsCode", + "inlineCode": [ + "const object = objects[0];\r", + "const startOpacity = eventsFunctionContext.getArgument(\"Value\");\r", + "\r", + "object.__particleEmitterAdapter.setStartOpacity(startOpacity);" + ], + "parameterObjects": "Object", + "useStrict": true, + "eventsSheetExpanded": false + } + ], + "parameters": [ + { + "description": "Object", + "name": "Object", + "supplementaryInformation": "ParticleEmitter3D::ParticleEmitter3D", + "type": "object" + } + ], + "objectGroups": [] + }, + { + "description": "the end opacity of the object.", + "fullName": "End opacity", + "functionType": "ExpressionAndCondition", + "group": "3D particle emitter color configuration", + "name": "EndOpacity", + "sentence": "the end opacity", + "events": [ + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [], + "actions": [ + { + "type": { + "value": "SetReturnNumber" + }, + "parameters": [ + "EndOpacity" + ] + } + ] + } + ], + "expressionType": { + "type": "expression" + }, + "parameters": [ + { + "description": "Object", + "name": "Object", + "supplementaryInformation": "ParticleEmitter3D::ParticleEmitter3D", + "type": "object" + } + ], + "objectGroups": [] + }, + { + "fullName": "", + "functionType": "ActionWithOperator", + "getterName": "EndOpacity", + "group": "3D particle emitter color configuration", + "name": "SetEndOpacity", + "sentence": "", + "events": [ + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [], + "actions": [ + { + "type": { + "value": "SetNumberVariable" + }, + "parameters": [ + "EndOpacity", + "=", + "Value" + ] + } + ] + }, + { + "type": "BuiltinCommonInstructions::JsCode", + "inlineCode": [ + "const object = objects[0];\r", + "const endOpacity = eventsFunctionContext.getArgument(\"Value\");\r", + "\r", + "object.__particleEmitterAdapter.setEndOpacity(endOpacity);" + ], + "parameterObjects": "Object", + "useStrict": true, + "eventsSheetExpanded": false + } + ], + "parameters": [ + { + "description": "Object", + "name": "Object", + "supplementaryInformation": "ParticleEmitter3D::ParticleEmitter3D", + "type": "object" + } + ], + "objectGroups": [] + }, + { + "description": "the flow of particles of the object (particles per second).", + "fullName": "Flow of particles", + "functionType": "ExpressionAndCondition", + "group": "3D particle emitter configuration", + "name": "Flow", + "sentence": "the flow of particles", + "events": [ + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [], + "actions": [ + { + "type": { + "value": "SetReturnNumber" + }, + "parameters": [ + "Flow" + ] + } + ] + } + ], + "expressionType": { + "type": "expression" + }, + "parameters": [ + { + "description": "Object", + "name": "Object", + "supplementaryInformation": "ParticleEmitter3D::ParticleEmitter3D", + "type": "object" + } + ], + "objectGroups": [] + }, + { + "fullName": "", + "functionType": "ActionWithOperator", + "getterName": "Flow", + "group": "3D particle emitter configuration", + "name": "SetFlow", + "sentence": "", + "events": [ + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [], + "actions": [ + { + "type": { + "value": "SetNumberVariable" + }, + "parameters": [ + "Flow", + "=", + "Value" + ] + } + ] + }, + { + "type": "BuiltinCommonInstructions::JsCode", + "inlineCode": [ + "const object = objects[0];\r", + "const flow = eventsFunctionContext.getArgument(\"Value\");\r", + "\r", + "object.__particleEmitterAdapter.setFlow(flow);\r", + "" + ], + "parameterObjects": "Object", + "useStrict": true, + "eventsSheetExpanded": false + } + ], + "parameters": [ + { + "description": "Object", + "name": "Object", + "supplementaryInformation": "ParticleEmitter3D::ParticleEmitter3D", + "type": "object" + } + ], + "objectGroups": [] + }, + { + "description": "the start min size of the object.", + "fullName": "Start min size", + "functionType": "ExpressionAndCondition", + "group": "3D particle emitter configuration", + "name": "StartSizeMin", + "sentence": "the start min size", + "events": [ + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [], + "actions": [ + { + "type": { + "value": "SetReturnNumber" + }, + "parameters": [ + "StartSizeMin" + ] + } + ] + } + ], + "expressionType": { + "type": "expression" + }, + "parameters": [ + { + "description": "Object", + "name": "Object", + "supplementaryInformation": "ParticleEmitter3D::ParticleEmitter3D", + "type": "object" + } + ], + "objectGroups": [] + }, + { + "fullName": "", + "functionType": "ActionWithOperator", + "getterName": "StartSizeMin", + "group": "3D particle emitter configuration", + "name": "SetStartSizeMin", + "sentence": "", + "events": [ + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [], + "actions": [ + { + "type": { + "value": "SetNumberVariable" + }, + "parameters": [ + "StartSizeMin", + "=", + "Value" + ] + } + ] + }, + { + "type": "BuiltinCommonInstructions::JsCode", + "inlineCode": [ + "const object = objects[0];\r", + "const startMinSize = eventsFunctionContext.getArgument(\"Value\");\r", + "\r", + "object.__particleEmitterAdapter.setStartMinSize(startMinSize);" + ], + "parameterObjects": "Object", + "useStrict": true, + "eventsSheetExpanded": false + } + ], + "parameters": [ + { + "description": "Object", + "name": "Object", + "supplementaryInformation": "ParticleEmitter3D::ParticleEmitter3D", + "type": "object" + } + ], + "objectGroups": [] + }, + { + "description": "the start max size of the object.", + "fullName": "Start max size", + "functionType": "ExpressionAndCondition", + "group": "3D particle emitter configuration", + "name": "StartSizeMax", + "sentence": "the start max size", + "events": [ + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [], + "actions": [ + { + "type": { + "value": "SetReturnNumber" + }, + "parameters": [ + "StartSizeMax" + ] + } + ] + } + ], + "expressionType": { + "type": "expression" + }, + "parameters": [ + { + "description": "Object", + "name": "Object", + "supplementaryInformation": "ParticleEmitter3D::ParticleEmitter3D", + "type": "object" + } + ], + "objectGroups": [] + }, + { + "fullName": "", + "functionType": "ActionWithOperator", + "getterName": "StartSizeMax", + "group": "3D particle emitter configuration", + "name": "SetStartSizeMax", + "sentence": "", + "events": [ + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [], + "actions": [ + { + "type": { + "value": "SetNumberVariable" + }, + "parameters": [ + "StartSizeMax", + "=", + "Value" + ] + } + ] + }, + { + "type": "BuiltinCommonInstructions::JsCode", + "inlineCode": [ + "const object = objects[0];\r", + "const startMaxSize = eventsFunctionContext.getArgument(\"Value\");\r", + "\r", + "object.__particleEmitterAdapter.setStartMaxSize(startMaxSize);\r", + "" + ], + "parameterObjects": "Object", + "useStrict": true, + "eventsSheetExpanded": false + } + ], + "parameters": [ + { + "description": "Object", + "name": "Object", + "supplementaryInformation": "ParticleEmitter3D::ParticleEmitter3D", + "type": "object" + } + ], + "objectGroups": [] + }, + { + "description": "the end scale of the object.", + "fullName": "End scale", + "functionType": "ExpressionAndCondition", + "group": "3D particle emitter configuration", + "name": "EndScale", + "sentence": "the end scale", + "events": [ + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [], + "actions": [ + { + "type": { + "value": "SetReturnNumber" + }, + "parameters": [ + "EndScale" + ] + } + ] + } + ], + "expressionType": { + "type": "expression" + }, + "parameters": [ + { + "description": "Object", + "name": "Object", + "supplementaryInformation": "ParticleEmitter3D::ParticleEmitter3D", + "type": "object" + } + ], + "objectGroups": [] + }, + { + "fullName": "", + "functionType": "ActionWithOperator", + "getterName": "EndScale", + "group": "3D particle emitter configuration", + "name": "SetEndScale", + "sentence": "", + "events": [ + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [], + "actions": [ + { + "type": { + "value": "SetNumberVariable" + }, + "parameters": [ + "EndScale", + "=", + "Value" + ] + } + ] + }, + { + "type": "BuiltinCommonInstructions::JsCode", + "inlineCode": [ + "const object = objects[0];\r", + "const endScale = eventsFunctionContext.getArgument(\"Value\");\r", + "\r", + "object.__particleEmitterAdapter.setEndScale(endScale);\r", + "" + ], + "parameterObjects": "Object", + "useStrict": true, + "eventsSheetExpanded": false + } + ], + "parameters": [ + { + "description": "Object", + "name": "Object", + "supplementaryInformation": "ParticleEmitter3D::ParticleEmitter3D", + "type": "object" + } + ], + "objectGroups": [] + }, + { + "description": "the min start speed of the object.", + "fullName": "Min start speed", + "functionType": "ExpressionAndCondition", + "group": "3D particle emitter speed configuration", + "name": "StartSpeedMin", + "sentence": "the min start speed", + "events": [ + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [], + "actions": [ + { + "type": { + "value": "SetReturnNumber" + }, + "parameters": [ + "StartSpeedMin" + ] + } + ] + } + ], + "expressionType": { + "type": "expression" + }, + "parameters": [ + { + "description": "Object", + "name": "Object", + "supplementaryInformation": "ParticleEmitter3D::ParticleEmitter3D", + "type": "object" + } + ], + "objectGroups": [] + }, + { + "fullName": "", + "functionType": "ActionWithOperator", + "getterName": "StartSpeedMin", + "group": "3D particle emitter speed configuration", + "name": "SetStartSpeedMin", + "sentence": "", + "events": [ + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [], + "actions": [ + { + "type": { + "value": "SetNumberVariable" + }, + "parameters": [ + "StartSpeedMin", + "=", + "Value" + ] + } + ] + }, + { + "type": "BuiltinCommonInstructions::JsCode", + "inlineCode": [ + "const object = objects[0];\r", + "const startSpeedMin = eventsFunctionContext.getArgument(\"Value\");\r", + "\r", + "object.__particleEmitterAdapter.setStartSpeedMin(startSpeedMin);" + ], + "parameterObjects": "Object", + "useStrict": true, + "eventsSheetExpanded": false + } + ], + "parameters": [ + { + "description": "Object", + "name": "Object", + "supplementaryInformation": "ParticleEmitter3D::ParticleEmitter3D", + "type": "object" + } + ], + "objectGroups": [] + }, + { + "description": "the max start speed of the object.", + "fullName": "Max start speed", + "functionType": "ExpressionAndCondition", + "group": "3D particle emitter speed configuration", + "name": "StartSpeedMax", + "sentence": "the max start speed", + "events": [ + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [], + "actions": [ + { + "type": { + "value": "SetReturnNumber" + }, + "parameters": [ + "StartSpeedMax" + ] + } + ] + } + ], + "expressionType": { + "type": "expression" + }, + "parameters": [ + { + "description": "Object", + "name": "Object", + "supplementaryInformation": "ParticleEmitter3D::ParticleEmitter3D", + "type": "object" + } + ], + "objectGroups": [] + }, + { + "fullName": "", + "functionType": "ActionWithOperator", + "getterName": "StartSpeedMax", + "group": "3D particle emitter speed configuration", + "name": "SetStartSpeedMax", + "sentence": "", + "events": [ + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [], + "actions": [ + { + "type": { + "value": "SetNumberVariable" + }, + "parameters": [ + "StartSpeedMax", + "=", + "Value" + ] + } + ] + }, + { + "type": "BuiltinCommonInstructions::JsCode", + "inlineCode": [ + "const object = objects[0];\r", + "const startSpeedMax = eventsFunctionContext.getArgument(\"Value\");\r", + "\r", + "object.__particleEmitterAdapter.setStartSpeedMax(startSpeedMax);" + ], + "parameterObjects": "Object", + "useStrict": true, + "eventsSheetExpanded": false + } + ], + "parameters": [ + { + "description": "Object", + "name": "Object", + "supplementaryInformation": "ParticleEmitter3D::ParticleEmitter3D", + "type": "object" + } + ], + "objectGroups": [] + }, + { + "description": "the min lifespan of the object.", + "fullName": "Min lifespan", + "functionType": "ExpressionAndCondition", + "group": "3D particle emitter configuration", + "name": "LifespanMin", + "sentence": "the min lifespan", + "events": [ + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [], + "actions": [ + { + "type": { + "value": "SetReturnNumber" + }, + "parameters": [ + "LifespanMin" + ] + } + ] + } + ], + "expressionType": { + "type": "expression" + }, + "parameters": [ + { + "description": "Object", + "name": "Object", + "supplementaryInformation": "ParticleEmitter3D::ParticleEmitter3D", + "type": "object" + } + ], + "objectGroups": [] + }, + { + "fullName": "", + "functionType": "ActionWithOperator", + "getterName": "LifespanMin", + "group": "3D particle emitter configuration", + "name": "SetLifespanMin", + "sentence": "", + "events": [ + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [], + "actions": [ + { + "type": { + "value": "SetNumberVariable" + }, + "parameters": [ + "LifespanMin", + "=", + "Value" + ] + } + ] + }, + { + "type": "BuiltinCommonInstructions::JsCode", + "inlineCode": [ + "const object = objects[0];\r", + "const lifespanMin = eventsFunctionContext.getArgument(\"Value\");\r", + "\r", + "object.__particleEmitterAdapter.setLifespanMin(lifespanMin);\r", + "" + ], + "parameterObjects": "Object", + "useStrict": true, + "eventsSheetExpanded": false + } + ], + "parameters": [ + { + "description": "Object", + "name": "Object", + "supplementaryInformation": "ParticleEmitter3D::ParticleEmitter3D", + "type": "object" + } + ], + "objectGroups": [] + }, + { + "description": "the max lifespan of the object.", + "fullName": "Max lifespan", + "functionType": "ExpressionAndCondition", + "group": "3D particle emitter configuration", + "name": "LifespanMax", + "sentence": "the max lifespan", + "events": [ + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [], + "actions": [ + { + "type": { + "value": "SetReturnNumber" + }, + "parameters": [ + "LifespanMax" + ] + } + ] + } + ], + "expressionType": { + "type": "expression" + }, + "parameters": [ + { + "description": "Object", + "name": "Object", + "supplementaryInformation": "ParticleEmitter3D::ParticleEmitter3D", + "type": "object" + } + ], + "objectGroups": [] + }, + { + "fullName": "", + "functionType": "ActionWithOperator", + "getterName": "LifespanMax", + "group": "3D particle emitter configuration", + "name": "SetLifespanMax", + "sentence": "", + "events": [ + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [], + "actions": [ + { + "type": { + "value": "SetNumberVariable" + }, + "parameters": [ + "LifespanMax", + "=", + "Value" + ] + } + ] + }, + { + "type": "BuiltinCommonInstructions::JsCode", + "inlineCode": [ + "const object = objects[0];\r", + "const lifespanMax = eventsFunctionContext.getArgument(\"Value\");\r", + "\r", + "object.__particleEmitterAdapter.setLifespanMax(lifespanMax);\r", + "" + ], + "parameterObjects": "Object", + "useStrict": true, + "eventsSheetExpanded": false + } + ], + "parameters": [ + { + "description": "Object", + "name": "Object", + "supplementaryInformation": "ParticleEmitter3D::ParticleEmitter3D", + "type": "object" + } + ], + "objectGroups": [] + }, + { + "description": "the emission duration of the object.", + "fullName": "Emission duration", + "functionType": "ExpressionAndCondition", + "group": "3D particle emitter configuration", + "name": "Duration", + "sentence": "the emission duration", + "events": [ + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [], + "actions": [ + { + "type": { + "value": "SetReturnNumber" + }, + "parameters": [ + "Duration" + ] + } + ] + } + ], + "expressionType": { + "type": "expression" + }, + "parameters": [ + { + "description": "Object", + "name": "Object", + "supplementaryInformation": "ParticleEmitter3D::ParticleEmitter3D", + "type": "object" + } + ], + "objectGroups": [] + }, + { + "fullName": "", + "functionType": "ActionWithOperator", + "getterName": "Duration", + "group": "3D particle emitter configuration", + "name": "SetDuration", + "sentence": "", + "events": [ + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [], + "actions": [ + { + "type": { + "value": "SetNumberVariable" + }, + "parameters": [ + "Duration", + "=", + "Value" + ] + } + ] + }, + { + "type": "BuiltinCommonInstructions::JsCode", + "inlineCode": [ + "const object = objects[0];\r", + "const duration = eventsFunctionContext.getArgument(\"Value\");\r", + "\r", + "object.__particleEmitterAdapter.setDuration(duration);\r", + "" + ], + "parameterObjects": "Object", + "useStrict": true, + "eventsSheetExpanded": false + } + ], + "parameters": [ + { + "description": "Object", + "name": "Object", + "supplementaryInformation": "ParticleEmitter3D::ParticleEmitter3D", + "type": "object" + } + ], + "objectGroups": [] + }, + { + "description": "Check if particles move with the emitter.", + "fullName": "Particles move with the emitter", + "functionType": "Condition", + "group": "3D particle emitter speed configuration", + "name": "AreParticlesRelative", + "sentence": "_PARAM0_ particles move with the emitter", + "events": [ + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [ + { + "type": { + "value": "BooleanVariable" + }, + "parameters": [ + "AreParticlesRelative", + "True", + "" + ] + } + ], + "actions": [ + { + "type": { + "value": "SetReturnBoolean" + }, + "parameters": [ + "True" + ] + } + ] + } + ], + "parameters": [ + { + "description": "Object", + "name": "Object", + "supplementaryInformation": "ParticleEmitter3D::ParticleEmitter3D", + "type": "object" + } + ], + "objectGroups": [] + }, + { + "description": "Change if particles move with the emitter.", + "fullName": "Particles move with the emitter", + "functionType": "Action", + "group": "3D particle emitter speed configuration", + "name": "SetAreParticlesRelative", + "sentence": "_PARAM0_ particles move with the emitter: _PARAM1_", + "events": [ + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [ + { + "type": { + "inverted": true, + "value": "BooleanVariable" + }, + "parameters": [ + "Value", + "True", + "" + ] + } + ], + "actions": [ + { + "type": { + "value": "SetBooleanVariable" + }, + "parameters": [ + "AreParticlesRelative", + "False", + "" + ] + } + ] + }, + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [ + { + "type": { + "value": "BooleanVariable" + }, + "parameters": [ + "Value", + "True", + "" + ] + } + ], + "actions": [ + { + "type": { + "value": "SetBooleanVariable" + }, + "parameters": [ + "AreParticlesRelative", + "True", + "" + ] + } + ] + }, + { + "type": "BuiltinCommonInstructions::JsCode", + "inlineCode": [ + "const object = objects[0];\r", + "const areParticlesRelative = eventsFunctionContext.getArgument(\"Value\");\r", + "\r", + "object.__particleEmitterAdapter.setParticlesRelative(areParticlesRelative);\r", + "" + ], + "parameterObjects": "Object", + "useStrict": true, + "eventsSheetExpanded": false + } + ], + "parameters": [ + { + "description": "Object", + "name": "Object", + "supplementaryInformation": "ParticleEmitter3D::ParticleEmitter3D", + "type": "object" + }, + { + "defaultValue": "yes", + "description": "AreParticlesRelative", + "name": "Value", + "optional": true, + "type": "yesorno" + } + ], + "objectGroups": [] + }, + { + "description": "the spay cone angle of the object.", + "fullName": "Spay cone angle", + "functionType": "ExpressionAndCondition", + "group": "3D particle emitter configuration", + "name": "SpayConeAngle", + "sentence": "the spay cone angle", + "events": [ + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [], + "actions": [ + { + "type": { + "value": "SetReturnNumber" + }, + "parameters": [ + "SpayConeAngle" + ] + } + ] + } + ], + "expressionType": { + "type": "expression" + }, + "parameters": [ + { + "description": "Object", + "name": "Object", + "supplementaryInformation": "ParticleEmitter3D::ParticleEmitter3D", + "type": "object" + } + ], + "objectGroups": [] + }, + { + "fullName": "", + "functionType": "ActionWithOperator", + "getterName": "SpayConeAngle", + "group": "3D particle emitter configuration", + "name": "SetSpayConeAngle", + "sentence": "", + "events": [ + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [], + "actions": [ + { + "type": { + "value": "SetNumberVariable" + }, + "parameters": [ + "SpayConeAngle", + "=", + "Value" + ] + } + ] + }, + { + "type": "BuiltinCommonInstructions::JsCode", + "inlineCode": [ + "const object = objects[0];\r", + "const sprayConeAngle = eventsFunctionContext.getArgument(\"Value\");\r", + "\r", + "object.__particleEmitterAdapter.setSprayConeAngle(sprayConeAngle);\r", + "" + ], + "parameterObjects": "Object", + "useStrict": true, + "eventsSheetExpanded": true + } + ], + "parameters": [ + { + "description": "Object", + "name": "Object", + "supplementaryInformation": "ParticleEmitter3D::ParticleEmitter3D", + "type": "object" + } + ], + "objectGroups": [] + }, + { + "description": "the blending of the object.", + "fullName": "Blending", + "functionType": "ExpressionAndCondition", + "group": "3D particle emitter color configuration", + "name": "Blending", + "sentence": "the blending", + "events": [ + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [], + "actions": [ + { + "type": { + "value": "SetReturnString" + }, + "parameters": [ + "Blending" + ] + } + ] + } + ], + "expressionType": { + "supplementaryInformation": "[\"Normal\",\"Additive\",\"Substractive\",\"Multiply\",\"None\"]", + "type": "stringWithSelector" + }, + "parameters": [ + { + "description": "Object", + "name": "Object", + "supplementaryInformation": "ParticleEmitter3D::ParticleEmitter3D", + "type": "object" + } + ], + "objectGroups": [] + }, + { + "fullName": "", + "functionType": "ActionWithOperator", + "getterName": "Blending", + "group": "3D particle emitter color configuration", + "name": "SetBlending", + "private": true, + "sentence": "", + "events": [ + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [], + "actions": [ + { + "type": { + "value": "SetStringVariable" + }, + "parameters": [ + "Blending", + "=", + "Value" + ] + } + ] + }, + { + "type": "BuiltinCommonInstructions::JsCode", + "inlineCode": [ + "const object = objects[0];\r", + "const blending = eventsFunctionContext.getArgument(\"Value\");\r", + "\r", + "object.__particleEmitterAdapter.setBlending(blending);\r", + "" + ], + "parameterObjects": "Object", + "useStrict": true, + "eventsSheetExpanded": true + } + ], + "parameters": [ + { + "description": "Object", + "name": "Object", + "supplementaryInformation": "ParticleEmitter3D::ParticleEmitter3D", + "type": "object" + } + ], + "objectGroups": [] + }, + { + "description": "the gravity top of the object.", + "fullName": "Gravity top", + "functionType": "ExpressionAndCondition", + "group": "3D particle emitter speed configuration", + "name": "GravityTop", + "sentence": "the gravity top", + "events": [ + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [], + "actions": [ + { + "type": { + "value": "SetReturnString" + }, + "parameters": [ + "GravityTop" + ] + } + ] + } + ], + "expressionType": { + "supplementaryInformation": "[\"Y-\",\"Z+\"]", + "type": "stringWithSelector" + }, + "parameters": [ + { + "description": "Object", + "name": "Object", + "supplementaryInformation": "ParticleEmitter3D::ParticleEmitter3D", + "type": "object" + } + ], + "objectGroups": [] + }, + { + "fullName": "", + "functionType": "ActionWithOperator", + "getterName": "GravityTop", + "group": "3D particle emitter speed configuration", + "name": "SetGravityTop", + "sentence": "", + "events": [ + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [], + "actions": [ + { + "type": { + "value": "SetStringVariable" + }, + "parameters": [ + "GravityTop", + "=", + "Value" + ] + } + ] + }, + { + "type": "BuiltinCommonInstructions::JsCode", + "inlineCode": [ + "const object = objects[0];\r", + "const gravityTop = eventsFunctionContext.getArgument(\"Value\");\r", + "\r", + "object.__particleEmitterAdapter.setGravityTop(gravityTop);\r", + "" + ], + "parameterObjects": "Object", + "useStrict": true, + "eventsSheetExpanded": true + } + ], + "parameters": [ + { + "description": "Object", + "name": "Object", + "supplementaryInformation": "ParticleEmitter3D::ParticleEmitter3D", + "type": "object" + } + ], + "objectGroups": [] + }, + { + "description": "the gravity of the object.", + "fullName": "Gravity", + "functionType": "ExpressionAndCondition", + "group": "3D particle emitter speed configuration", + "name": "Gravity", + "sentence": "the gravity", + "events": [ + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [], + "actions": [ + { + "type": { + "value": "SetReturnNumber" + }, + "parameters": [ + "Gravity" + ] + } + ] + } + ], + "expressionType": { + "type": "expression" + }, + "parameters": [ + { + "description": "Object", + "name": "Object", + "supplementaryInformation": "ParticleEmitter3D::ParticleEmitter3D", + "type": "object" + } + ], + "objectGroups": [] + }, + { + "fullName": "", + "functionType": "ActionWithOperator", + "getterName": "Gravity", + "group": "3D particle emitter speed configuration", + "name": "SetGravity", + "sentence": "", + "events": [ + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [], + "actions": [ + { + "type": { + "value": "SetNumberVariable" + }, + "parameters": [ + "Gravity", + "=", + "Value" + ] + } + ] + }, + { + "type": "BuiltinCommonInstructions::JsCode", + "inlineCode": [ + "const object = objects[0];\r", + "const gravity = eventsFunctionContext.getArgument(\"Value\");\r", + "\r", + "object.__particleEmitterAdapter.setGravity(gravity);\r", + "" + ], + "parameterObjects": "Object", + "useStrict": true, + "eventsSheetExpanded": true + } + ], + "parameters": [ + { + "description": "Object", + "name": "Object", + "supplementaryInformation": "ParticleEmitter3D::ParticleEmitter3D", + "type": "object" + } + ], + "objectGroups": [] + }, + { + "description": "Check if delete when emission ends.", + "fullName": "Delete when emission ends", + "functionType": "Condition", + "group": "3D particle emitter configuration", + "name": "ShouldAutodestruct", + "sentence": "_PARAM0_ delete when emission ends", + "events": [ + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [ + { + "type": { + "value": "BooleanVariable" + }, + "parameters": [ + "ShouldAutodestruct", + "True", + "" + ] + } + ], + "actions": [ + { + "type": { + "value": "SetReturnBoolean" + }, + "parameters": [ + "True" + ] + } + ] + } + ], + "parameters": [ + { + "description": "Object", + "name": "Object", + "supplementaryInformation": "ParticleEmitter3D::ParticleEmitter3D", + "type": "object" + } + ], + "objectGroups": [] + }, + { + "description": "Change if delete when emission ends.", + "fullName": "Delete when emission ends", + "functionType": "Action", + "group": "3D particle emitter configuration", + "name": "SetShouldAutodestruct", + "sentence": "_PARAM0_ delete when emission ends: _PARAM1_", + "events": [ + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [ + { + "type": { + "inverted": true, + "value": "BooleanVariable" + }, + "parameters": [ + "Value", + "True", + "" + ] + } + ], + "actions": [ + { + "type": { + "value": "SetBooleanVariable" + }, + "parameters": [ + "ShouldAutodestruct", + "False", + "" + ] + } + ] + }, + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [ + { + "type": { + "value": "BooleanVariable" + }, + "parameters": [ + "Value", + "True", + "" + ] + } + ], + "actions": [ + { + "type": { + "value": "SetBooleanVariable" + }, + "parameters": [ + "ShouldAutodestruct", + "True", + "" + ] + } + ] + } + ], + "parameters": [ + { + "description": "Object", + "name": "Object", + "supplementaryInformation": "ParticleEmitter3D::ParticleEmitter3D", + "type": "object" + }, + { + "defaultValue": "yes", + "description": "ShouldAutodestruct", + "name": "Value", + "optional": true, + "type": "yesorno" + } + ], + "objectGroups": [] + }, + { + "description": "the start min trail length of the object.", + "fullName": "Start min trail length", + "functionType": "ExpressionAndCondition", + "group": "3D particle emitter trail configuration", + "name": "TrailStartLengthMin", + "sentence": "the start min trail length", + "events": [ + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [], + "actions": [ + { + "type": { + "value": "SetReturnNumber" + }, + "parameters": [ + "TrailStartLengthMin" + ] + } + ] + } + ], + "expressionType": { + "type": "expression" + }, + "parameters": [ + { + "description": "Object", + "name": "Object", + "supplementaryInformation": "ParticleEmitter3D::ParticleEmitter3D", + "type": "object" + } + ], + "objectGroups": [] + }, + { + "fullName": "", + "functionType": "ActionWithOperator", + "getterName": "TrailStartLengthMin", + "group": "3D particle emitter trail configuration", + "name": "SetTrailStartLengthMin", + "sentence": "", + "events": [ + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [], + "actions": [ + { + "type": { + "value": "SetNumberVariable" + }, + "parameters": [ + "TrailStartLengthMin", + "=", + "Value" + ] + } + ] + }, + { + "type": "BuiltinCommonInstructions::JsCode", + "inlineCode": [ + "const object = objects[0];\r", + "const value = eventsFunctionContext.getArgument(\"Value\");\r", + "\r", + "object.__particleEmitterAdapter.setTrailStartLengthMin(value);" + ], + "parameterObjects": "Object", + "useStrict": true, + "eventsSheetExpanded": false + } + ], + "parameters": [ + { + "description": "Object", + "name": "Object", + "supplementaryInformation": "ParticleEmitter3D::ParticleEmitter3D", + "type": "object" + } + ], + "objectGroups": [] + }, + { + "description": "the start max trail length of the object.", + "fullName": "Start max trail length", + "functionType": "ExpressionAndCondition", + "group": "3D particle emitter trail configuration", + "name": "TrailStartLengthMax", + "sentence": "the start max trail length", + "events": [ + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [], + "actions": [ + { + "type": { + "value": "SetReturnNumber" + }, + "parameters": [ + "TrailStartLengthMax" + ] + } + ] + } + ], + "expressionType": { + "type": "expression" + }, + "parameters": [ + { + "description": "Object", + "name": "Object", + "supplementaryInformation": "ParticleEmitter3D::ParticleEmitter3D", + "type": "object" + } + ], + "objectGroups": [] + }, + { + "fullName": "", + "functionType": "ActionWithOperator", + "getterName": "TrailStartLengthMax", + "group": "3D particle emitter trail configuration", + "name": "SetTrailStartLengthMax", + "sentence": "", + "events": [ + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [], + "actions": [ + { + "type": { + "value": "SetNumberVariable" + }, + "parameters": [ + "TrailStartLengthMax", + "=", + "Value" + ] + } + ] + }, + { + "type": "BuiltinCommonInstructions::JsCode", + "inlineCode": [ + "const object = objects[0];\r", + "const value = eventsFunctionContext.getArgument(\"Value\");\r", + "\r", + "object.__particleEmitterAdapter.setTrailStartLengthMax(value);" + ], + "parameterObjects": "Object", + "useStrict": true, + "eventsSheetExpanded": false + } + ], + "parameters": [ + { + "description": "Object", + "name": "Object", + "supplementaryInformation": "ParticleEmitter3D::ParticleEmitter3D", + "type": "object" + } + ], + "objectGroups": [] + }, + { + "description": "Check if the trail should follow the object.", + "fullName": "Trail following the object", + "functionType": "Condition", + "group": "3D particle emitter trail configuration", + "name": "IsTrailFollowingLocalOrigin", + "sentence": "The trail is following _PARAM0_ ", + "events": [ + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [ + { + "type": { + "value": "BooleanVariable" + }, + "parameters": [ + "IsTrailFollowingLocalOrigin", + "True", + "" + ] + } + ], + "actions": [ + { + "type": { + "value": "SetReturnBoolean" + }, + "parameters": [ + "True" + ] + } + ] + } + ], + "parameters": [ + { + "description": "Object", + "name": "Object", + "supplementaryInformation": "ParticleEmitter3D::ParticleEmitter3D", + "type": "object" + } + ], + "objectGroups": [] + }, + { + "description": "Change if the trail should follow the object or not.", + "fullName": "Make the trail follow", + "functionType": "Action", + "group": "3D particle emitter trail configuration", + "name": "SetIsTrailFollowingLocalOrigin", + "sentence": "Make the trail follow _PARAM0_: _PARAM1_", + "events": [ + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [ + { + "type": { + "value": "BooleanVariable" + }, + "parameters": [ + "Value", + "False", + "" + ] + } + ], + "actions": [ + { + "type": { + "value": "SetBooleanVariable" + }, + "parameters": [ + "IsTrailFollowingLocalOrigin", + "False", + "" + ] + } + ] + }, + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [ + { + "type": { + "value": "BooleanVariable" + }, + "parameters": [ + "Value", + "True", + "" + ] + } + ], + "actions": [ + { + "type": { + "value": "SetBooleanVariable" + }, + "parameters": [ + "IsTrailFollowingLocalOrigin", + "True", + "" + ] + } + ] + }, + { + "type": "BuiltinCommonInstructions::JsCode", + "inlineCode": [ + "const object = objects[0];\r", + "const value = eventsFunctionContext.getArgument(\"Value\");\r", + "\r", + "object.__particleEmitterAdapter.setTrailFollowingLocalOrigin(value);" + ], + "parameterObjects": "Object", + "useStrict": true, + "eventsSheetExpanded": false + } + ], + "parameters": [ + { + "description": "Object", + "name": "Object", + "supplementaryInformation": "ParticleEmitter3D::ParticleEmitter3D", + "type": "object" + }, + { + "defaultValue": "yes", + "description": "IsTrailFollowingLocalOrigin", + "name": "Value", + "optional": true, + "type": "yesorno" + } + ], + "objectGroups": [] + }, + { + "description": "the tail end width ratio of the object.", + "fullName": "Tail end width ratio", + "functionType": "ExpressionAndCondition", + "group": "3D particle emitter trail configuration", + "name": "TrailEndWidthRatio", + "sentence": "the tail end width ratio", + "events": [ + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [], + "actions": [ + { + "type": { + "value": "SetReturnNumber" + }, + "parameters": [ + "TrailEndWidthRatio" + ] + } + ] + } + ], + "expressionType": { + "type": "expression" + }, + "parameters": [ + { + "description": "Object", + "name": "Object", + "supplementaryInformation": "ParticleEmitter3D::ParticleEmitter3D", + "type": "object" + } + ], + "objectGroups": [] + }, + { + "fullName": "", + "functionType": "ActionWithOperator", + "getterName": "TrailEndWidthRatio", + "group": "3D particle emitter trail configuration", + "name": "SetTrailEndWidthRatio", + "sentence": "", + "events": [ + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [], + "actions": [ + { + "type": { + "value": "SetNumberVariable" + }, + "parameters": [ + "TrailEndWidthRatio", + "=", + "Value" + ] + } + ] + }, + { + "type": "BuiltinCommonInstructions::JsCode", + "inlineCode": [ + "const object = objects[0];\r", + "const value = eventsFunctionContext.getArgument(\"Value\");\r", + "\r", + "object.__particleEmitterAdapter.setTrailEndWidthRatio(value);" + ], + "parameterObjects": "Object", + "useStrict": true, + "eventsSheetExpanded": false + } + ], + "parameters": [ + { + "description": "Object", + "name": "Object", + "supplementaryInformation": "ParticleEmitter3D::ParticleEmitter3D", + "type": "object" + } + ], + "objectGroups": [] + }, + { + "description": "the render mode of the object.", + "fullName": "Render mode", + "functionType": "ExpressionAndCondition", + "group": "3D particle emitter configuration", + "name": "RenderMode", + "private": true, + "sentence": "the render mode", + "events": [ + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [], + "actions": [ + { + "type": { + "value": "SetReturnString" + }, + "parameters": [ + "RenderMode" + ] + } + ] + } + ], + "expressionType": { + "supplementaryInformation": "[\"Billboard\",\"Trail\"]", + "type": "stringWithSelector" + }, + "parameters": [ + { + "description": "Object", + "name": "Object", + "supplementaryInformation": "ParticleEmitter3D::ParticleEmitter3D", + "type": "object" + } + ], + "objectGroups": [] + }, + { + "fullName": "", + "functionType": "ActionWithOperator", + "getterName": "RenderMode", + "group": "3D particle emitter configuration", + "name": "SetRenderMode", + "private": true, + "sentence": "", + "events": [ + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [], + "actions": [ + { + "type": { + "value": "SetStringVariable" + }, + "parameters": [ + "RenderMode", + "=", + "Value" + ] + } + ] + }, + { + "type": "BuiltinCommonInstructions::JsCode", + "inlineCode": [ + "const object = objects[0];\r", + "const value = eventsFunctionContext.getArgument(\"Value\");\r", + "\r", + "object.__particleEmitterAdapter.setRenderMode(value);" + ], + "parameterObjects": "Object", + "useStrict": true, + "eventsSheetExpanded": false + }, + { + "type": "BuiltinCommonInstructions::Comment", + "color": { + "b": 109, + "g": 230, + "r": 255, + "textB": 0, + "textG": 0, + "textR": 0 + }, + "comment": "Update the trail properties because the RenderMode setter reset them" + }, + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [ + { + "type": { + "value": "StringVariable" + }, + "parameters": [ + "RenderMode", + "=", + "\"Trail\"" + ] + } + ], + "actions": [ + { + "type": { + "value": "ParticleEmitter3D::ParticleEmitter3D::SetTrailStartLengthMin" + }, + "parameters": [ + "Object", + "=", + "TrailStartLengthMin", + "" + ] + }, + { + "type": { + "value": "ParticleEmitter3D::ParticleEmitter3D::SetTrailStartLengthMax" + }, + "parameters": [ + "Object", + "=", + "TrailStartLengthMax", + "" + ] + } + ] + } + ], + "parameters": [ + { + "description": "Object", + "name": "Object", + "supplementaryInformation": "ParticleEmitter3D::ParticleEmitter3D", + "type": "object" + } + ], + "objectGroups": [] + } + ], + "eventsFunctionsFolderStructure": { + "folderName": "__ROOT", + "children": [ + { + "functionName": "onCreated" + }, + { + "functionName": "onDestroy" + }, + { + "functionName": "onHotReloading" + }, + { + "functionName": "UpdateFromProperties" + }, + { + "functionName": "UpdateHelper" + }, + { + "functionName": "UpdateImage" + }, + { + "functionName": "doStepPostEvents" + }, + { + "functionName": "RegisterInLayer" + }, + { + "functionName": "Delete" + }, + { + "functionName": "HasEnded" + }, + { + "functionName": "Restart" + }, + { + "folderName": "Position", + "children": [ + { + "functionName": "Z" + }, + { + "functionName": "SetZ" + } + ] + }, + { + "folderName": "Angle", + "children": [ + { + "functionName": "RotationX" + }, + { + "functionName": "SetRotationX" + }, + { + "functionName": "RotationY" + }, + { + "functionName": "SetRotationY" + } + ] + }, + { + "folderName": "3D particle emitter color configuration", + "children": [ + { + "functionName": "StartColor" + }, + { + "functionName": "SetStartColor" + }, + { + "functionName": "EndColor" + }, + { + "functionName": "SetEndColor" + }, + { + "functionName": "StartOpacity" + }, + { + "functionName": "SetStartOpacity" + }, + { + "functionName": "EndOpacity" + }, + { + "functionName": "SetEndOpacity" + }, + { + "functionName": "Blending" + }, + { + "functionName": "SetBlending" + } + ] + }, + { + "folderName": "3D particle emitter configuration", + "children": [ + { + "functionName": "Flow" + }, + { + "functionName": "SetFlow" + }, + { + "functionName": "StartSizeMin" + }, + { + "functionName": "SetStartSizeMin" + }, + { + "functionName": "StartSizeMax" + }, + { + "functionName": "SetStartSizeMax" + }, + { + "functionName": "EndScale" + }, + { + "functionName": "SetEndScale" + }, + { + "functionName": "LifespanMin" + }, + { + "functionName": "SetLifespanMin" + }, + { + "functionName": "LifespanMax" + }, + { + "functionName": "SetLifespanMax" + }, + { + "functionName": "Duration" + }, + { + "functionName": "SetDuration" + }, + { + "functionName": "SpayConeAngle" + }, + { + "functionName": "SetSpayConeAngle" + }, + { + "functionName": "ShouldAutodestruct" + }, + { + "functionName": "SetShouldAutodestruct" + }, + { + "functionName": "RenderMode" + }, + { + "functionName": "SetRenderMode" + } + ] + }, + { + "folderName": "3D particle emitter speed configuration", + "children": [ + { + "functionName": "StartSpeedMin" + }, + { + "functionName": "SetStartSpeedMin" + }, + { + "functionName": "StartSpeedMax" + }, + { + "functionName": "SetStartSpeedMax" + }, + { + "functionName": "AreParticlesRelative" + }, + { + "functionName": "SetAreParticlesRelative" + }, + { + "functionName": "GravityTop" + }, + { + "functionName": "SetGravityTop" + }, + { + "functionName": "Gravity" + }, + { + "functionName": "SetGravity" + } + ] + }, + { + "folderName": "3D particle emitter trail configuration", + "children": [ + { + "functionName": "TrailStartLengthMin" + }, + { + "functionName": "SetTrailStartLengthMin" + }, + { + "functionName": "TrailStartLengthMax" + }, + { + "functionName": "SetTrailStartLengthMax" + }, + { + "functionName": "IsTrailFollowingLocalOrigin" + }, + { + "functionName": "SetIsTrailFollowingLocalOrigin" + }, + { + "functionName": "TrailEndWidthRatio" + }, + { + "functionName": "SetTrailEndWidthRatio" + } + ] + } + ] + }, + "propertyDescriptors": [ + { + "value": "255;0;0", + "type": "Color", + "label": "Start color", + "group": "Color", + "name": "StartColor" + }, + { + "value": "255;255;0", + "type": "Color", + "label": "End color", + "group": "Color", + "name": "EndColor" + }, + { + "value": "255", + "type": "Number", + "unit": "Dimensionless", + "label": "Start opacity", + "group": "Color", + "name": "StartOpacity" + }, + { + "value": "0", + "type": "Number", + "unit": "Dimensionless", + "label": "End opacity", + "group": "Color", + "name": "EndOpacity" + }, + { + "value": "50", + "type": "Number", + "label": "Flow of particles (particles per second)", + "name": "Flow" + }, + { + "value": "10", + "type": "Number", + "unit": "Pixel", + "label": "Start min size", + "group": "Size", + "name": "StartSizeMin" + }, + { + "value": "20", + "type": "Number", + "unit": "Pixel", + "label": "Start max size", + "group": "Size", + "name": "StartSizeMax" + }, + { + "value": "0", + "type": "Number", + "unit": "Dimensionless", + "label": "End scale", + "group": "Size", + "name": "EndScale" + }, + { + "value": "100", + "type": "Number", + "unit": "PixelSpeed", + "label": "Start min speed", + "group": "Speed", + "name": "StartSpeedMin" + }, + { + "value": "100", + "type": "Number", + "unit": "PixelSpeed", + "label": "Start max speed", + "group": "Speed", + "name": "StartSpeedMax" + }, + { + "value": "1", + "type": "Number", + "unit": "Second", + "label": "Min lifespan", + "name": "LifespanMin" + }, + { + "value": "2", + "type": "Number", + "unit": "Second", + "label": "Max lifespan", + "name": "LifespanMax" + }, + { + "value": "5", + "type": "Number", + "unit": "Second", + "label": "Emission duration", + "name": "Duration" + }, + { + "value": "", + "type": "Boolean", + "label": "Particles move with the emitter", + "group": "Speed", + "name": "AreParticlesRelative" + }, + { + "value": "30", + "type": "Number", + "unit": "DegreeAngle", + "label": "Spay cone angle", + "group": "Position", + "name": "SpayConeAngle" + }, + { + "value": "Additive", + "type": "Choice", + "label": "Blending", + "group": "Color", + "choices": [ + { + "label": "Normal", + "value": "Normal" + }, + { + "label": "Additive", + "value": "Additive" + }, + { + "label": "Subtractive", + "value": "Subtractive" + }, + { + "label": "Multiply", + "value": "Multiply" + }, + { + "label": "None", + "value": "None" + } + ], + "name": "Blending" + }, + { + "value": "Y-", + "type": "Choice", + "label": "Gravity top", + "group": "Speed", + "choices": [ + { + "label": "Y-", + "value": "Y-" + }, + { + "label": "Z+", + "value": "Z+" + } + ], + "name": "GravityTop" + }, + { + "value": "0", + "type": "Number", + "unit": "PixelAcceleration", + "label": "Gravity", + "group": "Speed", + "name": "Gravity" + }, + { + "value": "true", + "type": "Boolean", + "label": "Delete when emission ends", + "name": "ShouldAutodestruct" + }, + { + "value": "Center-center", + "type": "String", + "label": "", + "description": "Only used by the scene editor.", + "hidden": true, + "name": "ParentOrigin" + }, + { + "value": "0", + "type": "Number", + "unit": "Pixel", + "label": "Z (elevation)", + "description": "Deprecated", + "group": "Position", + "hidden": true, + "name": "Z" + }, + { + "value": "0", + "type": "Number", + "unit": "DegreeAngle", + "label": "Rotation on X axis", + "group": "Position", + "hidden": true, + "name": "RotationX" + }, + { + "value": "0", + "type": "Number", + "unit": "DegreeAngle", + "label": "Rotation on Y axis", + "group": "Position", + "hidden": true, + "name": "RotationY" + }, + { + "value": "120", + "type": "Number", + "unit": "Pixel", + "label": "Start min length", + "group": "Trail", + "advanced": true, + "name": "TrailStartLengthMin" + }, + { + "value": "120", + "type": "Number", + "unit": "Pixel", + "label": "Start max length", + "group": "Trail", + "advanced": true, + "name": "TrailStartLengthMax" + }, + { + "value": "Billboard", + "type": "Choice", + "label": "Render mode", + "choices": [ + { + "label": "Image facing the camera", + "value": "Billboard" + }, + { + "label": "Image following a trail", + "value": "Trail" + } + ], + "advanced": true, + "name": "RenderMode" + }, + { + "value": "", + "type": "Boolean", + "label": "Follow the object", + "group": "Trail", + "advanced": true, + "name": "IsTrailFollowingLocalOrigin" + }, + { + "value": "0", + "type": "Number", + "unit": "Dimensionless", + "label": "Tail end width ratio", + "group": "Trail", + "advanced": true, + "name": "TrailEndWidthRatio" + } + ], + "propertiesFolderStructure": { + "folderName": "__ROOT", + "children": [ + { + "propertyName": "Flow" + }, + { + "propertyName": "LifespanMin" + }, + { + "propertyName": "LifespanMax" + }, + { + "propertyName": "Duration" + }, + { + "propertyName": "ShouldAutodestruct" + }, + { + "propertyName": "RenderMode" + }, + { + "folderName": "Color", + "children": [ + { + "propertyName": "StartColor" + }, + { + "propertyName": "EndColor" + }, + { + "propertyName": "StartOpacity" + }, + { + "propertyName": "EndOpacity" + }, + { + "propertyName": "Blending" + } + ] + }, + { + "folderName": "Size", + "children": [ + { + "propertyName": "StartSizeMin" + }, + { + "propertyName": "StartSizeMax" + }, + { + "propertyName": "EndScale" + } + ] + }, + { + "folderName": "Trail", + "children": [ + { + "propertyName": "TrailStartLengthMin" + }, + { + "propertyName": "TrailStartLengthMax" + }, + { + "propertyName": "TrailEndWidthRatio" + }, + { + "propertyName": "IsTrailFollowingLocalOrigin" + } + ] + }, + { + "folderName": "Speed", + "children": [ + { + "propertyName": "StartSpeedMin" + }, + { + "propertyName": "StartSpeedMax" + }, + { + "propertyName": "AreParticlesRelative" + }, + { + "propertyName": "GravityTop" + }, + { + "propertyName": "Gravity" + } + ] + }, + { + "folderName": "Position", + "children": [ + { + "propertyName": "SpayConeAngle" + }, + { + "propertyName": "Z" + }, + { + "propertyName": "RotationX" + }, + { + "propertyName": "RotationY" + } + ] + }, + { + "propertyName": "ParentOrigin" + } + ] + }, + "variants": [] + } + ] } ], "externalLayouts": [] diff --git a/examples/starting-3d-rts-unit-selection/starting-3d-rts-unit-selection.json b/examples/starting-3d-rts-unit-selection/starting-3d-rts-unit-selection.json index 9c945f819..b0e1af22f 100644 --- a/examples/starting-3d-rts-unit-selection/starting-3d-rts-unit-selection.json +++ b/examples/starting-3d-rts-unit-selection/starting-3d-rts-unit-selection.json @@ -1,7 +1,7 @@ { "firstLayout": "", "gdVersion": { - "build": 272, + "build": 274, "major": 5, "minor": 6, "revision": 0 diff --git a/examples/starting-3d-shootemup/assets/Collision.wav b/examples/starting-3d-shootemup/assets/Collision.wav new file mode 100644 index 0000000000000000000000000000000000000000..5b89dda9865db53229ee501b111b0612758f968b GIT binary patch literal 18870 zcmcKCc|26zA2{$qi;~htN!IL1Boawd2rZ~2>S^D%r}b%(C0a&GwiJ?5QK(28Eo2wj zx7nC6X2voYvoH5|&Ro;>^n8E6e}40Rz3!bE+`0G8x#ym9KA*$OZRN@=HwtZP&o>z{eFaq#%MCBv6+SBC?5W61#x|B@HGG zmT{n&12YHI2S)dd0S;3RQ;tw-aNmGTB6NWiNV-T;=p5BKmZ}IKcnm>~5YrIb5Z6#t z|E0dToKPEJuLB5VUu1veLD7O3z+D6#6dV*B5|H>Leg{8~m&bd}laoL z*cw3FU_0=z2RckTLOMz+A(RkG32Z8d%B60``{M)f<-`i2Uh|x0{pJ#KDZnN3fW7#= z_OHic`h;$}!{_@{D++ zxljk#vkv|XAygnKka!883Sot?Qn*VDeGovmhNA)QFb}xTy3a~wEfFjcI0)eOzH&D6 zHuJ`?#{##6&`}Y@mq4GypMk6VtNd&Hy8=jE1dSDr6)Fl%IdeJL{O5rB(5xYip>G_> zo(Cy%6glHKy?s#RAas2g>fl2DLVsa^&IK5i6TCF<$W5&nKkM|266g({0`+VQ?gU|Q9J@EF> z+eL-8g?5D@1)&9D1!@H|3e*c!-c5ct^_}q>(>G>s9KJ05(w$4sW#p#6efsv<+l04w z-X^})dZzPC_nGy5+xvF+xv7FwajN(c=CS_E8yEH}=1t7I zn2D5hxD%8VbT3Hry3X}E*AIss3p)|EXjDgg*** zid+%t8aWU>6wQv-ysUG1&SmRh+u$X^j={@=R|dNUdjzizz7mCvij10>q>-eVlzi>} zwTIW@t|VMZykdUU;;Plvpx}_;%fZ)zuLmoKsDw-nIgUMrJ%e?QSsCLNlX~OfjmI~p zhNy+8hkOYB6kHT+6SFvGNlZp;W^8t>SE^6y#?Ce)Ovr4lnvKFS>q}!%bK%u0kWu-k&%SqdIXZxL9cS2$= z#axbg5Rw-1B;-Y0Zd_j6lbDQ{%$N@$pF_Tcl!jD>REHEtmPJ-Z?vFkgeI&Z%LHUEK z2d`4!q`plpjjo9P8toIYF~Tn*^iuextCw8k-QzvuugBhqy%nn$svl|;Y8q-DY8kpP zbWx~XsKpJd8`d|pZtC2eb8~z6uJAqKudlqjl7D4K=} z_O+MSE{9$Vy&ifa{8o5u_|7Z4uk5>$8k!cG9x4ozgvo`!3Vjp$KD7GQw_EkM9wa|X zev*9j(eXzoA1NhIOrD&KO^HmonX)RyBgHeNG_5SHGR+}&S*&v`E)AbXN}K*b?ZM0k z%d?%cU9y#*PJF8JR4!{&*61wj_crerzfUN>Tbx*Yy5^snvo%)>v4s(Z{&@j;f92hM zmh>#;+5KlzpQ}Ef@w_tcYhHC;|4Zh}!I#My_cI=3?0&iL<$;%@Qpcu_OP!Ufk*byY z*OTo}c0SR2W$?=I)rr@qUY~ir{L_k0D?iPzwy3tOMw&3qa?P9TH`n{uUupMo-5;D@E`Pb= zWoCX>{`33=l~$G3l}#U8KDK^bQs7XqtiY*YV(FyP$)!)rpOt5ppKd(ec&4$7*hQog zUy<|3ugL`XqDz~kL((PPZA)xRYMVpR2hK1cb}zJ$Y)!TyYtwb;x^xB;3Sb4Wwy@lI zZoE}M74Iw1ENB6^5{QjJLsC=((!fCe5-1OWu1cVHLWm@Wm zrM9rP@HS!>sjH*QxWlBwv}0}uv60l+(HLHRx%x`=^_Iw%8!bMS8!9(e1{D2Ow5`bR z!{!eGAKddj^H=996pt<*TdY_-BY#%DM*j7p$f6rXcXIFMCgp|~UM{>=xUtB$$gilR zu&l7W@M;mZD55C+Q^u#vPg$R`b6@1X%pILEHe-B7cHZ;6oV<(=&pu>+V7+0#;l4@w zocuZE^VpnmIZ8RoIpUAVN6e>^oYI_%9A?&F)=<{1lQ$=C&)S)_J8N&& zjwidG?0u4&l$P{3X;Yei+Lp9M$%~VhBzIqDT<^Vp6B~_du1>74RUrWCRYcZW$b8Z>jin$Ye=k}eucV0hu_aOhlr0gl#)3O()+N3T{J(qAH zAt<3Pp)sK;;nm~UkKaAMnh=q2BOxlmD`tI+Z_L)1Z81AzW@0t5+E_iT0oE96ik*kG zz%Ilt!rEaSusfsoMDLGw!+K)ZVzm=>6ZI3(8j%~tj}k>8QEidc` z*fuNyYkSk~ro&A-winC94q-W1J~k;LB_cI~8cmPxjqZ-Z*AlOlo{;Lh9XAotJtq=e(>-YfNiSTkzQW@uJ7xIX*cXbN)#`mwq8VD7`AD zI;S@0er{^+!`wTWcQcbREfNQ18Equ!(6 zb4_+nUY$HPa$Mwu$l|+YcPs9Gy<3;wnBJW3@Y?b9^4Dt5)SqcQ+x*D?(bh-X9@S?w zWwd0ZWk1PI&rZom%}C4edgb$K{j2BMFSB#A^Rf+}nLL~O%=q!#$MYVWJT-f2{#5bp zxVK7gC%hFFiHgKUKA+cr-uRhTNv~v7Zm8Z^?OW|vom6wLCIwtCrZsbGNbRKdj`r5Z zw#N3xZ{@Y+_2mualF!Izxk8IV%R;NdwUz5Cy(<|7y#;**0|n7m7=4V@GFUYpeu6|qdw(hNO$)=LcCBs$hDsI*7uXn!Q{hIi-zM-L^v7xB_OM7wq zjQW}N>h`5Jeem?q8DL;&5I8jmRSZ`Q zR{|Bi5S>A1Fc{UO8qzm_P2o_u6xgaZ=-y26rvy;S2^EA&V1P788X_H~AEh6okEV{M zjsapiVmsnGVu^9U_14JN8?9f6#l#X~DN&9rPgWqu({BT!KCx8MaMAFWVIm(2=0eMb zsK??bfQA5?$AQc_CIcn|rUU2L=h)}j*m#&*Nv(gT{Wgku_~kaY4fvYB7sEcAPi6jfg#G({;U1h`W5@e0q~9p zA-za1rtcy>2oQljC3Ks4o0-5A(nWMJJ)|?V^HOJ6=TXpGJx1Du^8+^H=GR%&S=N22 zEvYT7J%~SqKMd@n?4#_bJZwt?8b}SKMp6@LPwU>+eXS{t_ZuHHW>#iZKCgUH`KIJ; z$@>z7>&_j!*Bo)n}PJS{M(G_9Ol*-_hBORnvz z-P^XWZGT&4T~=LoT}fGKS$SDS*~iAujfIWH((gRBEv_vd$iP3vKf_0M-vBz89n4N( z-k>>ftp9H*JQYtRP**dddQQD`w7^sPDtst>=b|iKrbsV4_q#Xj*QN5_% z)ClTzY9tlkkMAe+6Z_BiUg!<%wWZop7gO!10gNq-t&9tUfj|Tgy3U)=n$NOeedZTR zC5fPkf{B7j0woEmOaeU~VT(X%La0WJLdQ+K5Y#CQBrT+%pdP_Mfe7R$K`{kTw-9w- z3>_4r#L~wrKI9^Bl{z9k3Y_MljtGtjjtcB0s4hX5fGUs|$%E0F#1WJD(59hHLw)z4SoJMM@Cm0xl4M?Jc+3Rkg6SW8dsa@+7S$ts&J@8YqpF zSM7Q2Z`$9sYvQzVIyig{p@vvPs)?(Mue)70gD?}AMVL&RLYqok+PAdNvCp92px+Si zqx;b})A2L{K%~hN<%tT!MGdwMiyQ13HneO60-G*21vLdXDOQfJRH{7v<;<6}U(S8W z`k4Lk#mBQ%=c_JM1y%)?Un~zU$5vdgh^)9-QBzV^QeQ%<>!>5wQR*h)Rq&JX>zg(J zzD;FqJxC9xS0Eky5HZ{-9u!Z?Y@#Mn3n*(UYpQ6fY^wj( z@U7{aeEF#I(dCNe7a9T^E;g|1ICb1QUL73kDyiQ^*aPe(xKiDKX}!~XRe?HMJ*|P3 z+?(8cuQ#PPckmT3n?0MY3Dodvc;9%Y0@SbovK67s1@i$<3F?Fd3K?OIKVu+yw64V_LIU;B5YC1!vt3w_D{CHL&I%odE)8P_FavS`yh43?Uv$0bdI@Cq zWc6hCvRu09Mhi+p~tL0I8W1#6u)0L(HzLPgfQ73gxq5x*&&Bo6ik5`6p%J$>! zq}C}y<42=FwI($%W?&2xXdc}J_{*bmM!l5!G74IwxJGd`a7#H#s&?Erspv`3lWtAw zRDn8Fw5MxN*P5<5U2`^kxN5inD>a^LWCK|m@iXIQ#scM2%K(uQBveY6k}&1=l=vyr zRi^<{RbS420c7cC>Oa-b(4VL^QA=5Cg4QLY5TjtDAR{Hqah8gf#`BHl8_d_AKgW2E zv97U>v4(}Z#Y_t|i)l7fY$n^hb$H{D=kUrQ%rV3<$nm1%C{M(L@4@yM@ML&)c@jP8 zE4o&YR)o6;yPtDE?ao})v#M(qX;r$%LyvnN2_B0atsKoA6_!Zs1@;{KDyLGXBBu{d z5AE;S-?fjof8m(vnC|$(F4HdEF3oPW(<-MGPRpDW>=8SG9mh`5Nx=zO#$Tr5tmHh_ z*~-n_&BRULjqk#CVY)C}raDh@9`8KH*$=Z&zqLAI^_A6PH=f&&TfbYlOR&p1m(woOyeE1qdMkLB z`4swq0S2GaEuXi%-_q<`>s#Ub#W!I~^p@*e!nf#d(%3Y8)1*xb-eNDV*PxfX?+V|g zzP7&M{(=5y{57_yZkf1c>=wB#!hqp`z5r?fDWEN&KHzIWalprbHvtR0&Abi0b-XWn z{o{4qYlG)nPj}Cio+rExd+qbu;k72fHNYvrE}&t3_4=~)Me95K+x;8-zxo&Z7x=&S zf8mc^6Sn5!nlauOZ-Lja7knqf|AD`T-*mr8eu{pf{^$Hp`XBM%>%ZOK&)>&?wZDu1 zGJiY&VxNybZ+x`<)%~aVPw>~-G;`C`P0E|Lt=qhA{kk>lW;v@mPj;T*JkP8t8zWFJ<~lOY)sl1zcFf~-Da!JW}6K+>ujF2dFp26&0{uW zHVgb%e$@4(^=<1L*4O)0`W5?q^n2s?((jqyL%(Fdc)ut=tlt73Gao}A_q8k5I<8&3 zc7xwqzg2$A{g(JG^fUJ}_Dl6i^ojGi>7(c;?d}&?dIAU+J!E?xb*DOlS?;yZ17m?vC5;>wcfSH zwcK^L&tE=%K0ZDzo(-PWo@JgbBj053s&lJOuR69Wc4gE`?8?h4-+AYHXL&#IKIXgM zcZY9)Z@5>G*IBQVUL2nRAG#0OXYM+Kb=vFH*NyX4@D*(wy%t#`STnrF*Uih#!_CEQ z>IUTvqcryg;oi6v357@uy&z#@FyqjCfg|6jI$YSbISgh{XzS^ z_6aLvR^C{7ZKbo@Qn$r!R&F+q3moS;8ar-v+T^s}X^m6H>W8b7S0}8#?~>#a?-J$G z=u+cS;Zp2!)b4=Y9=jcOFKjYx(rwag(yi01?^`EYPc@%pu4F#W{H{^FQH;?oqh$t5 z4eSkU4H8UZO`=V1m}Kf?=seL$)3KO6f413d)7hG;8mhBYXR7W{+phMP+7`9+X^*Ep zn)Yy7fyO(HHyU{ww(2(O3)L5>f13VrdcpMf(;H^h&#IkOJj*=-qSQ!H&ZuNH&%C3U8%Z4)mb%2Gf?xq=0BPNYMa&k)HbR) z%&?zfH)GL^HtlBZM(uiS4fUDoGt^bpr>dLIG@fZV(_m)zEb1)std3bi4W0%^V_3se zdzH4U_6qG$bL8eAdLliXL5o47LA}8Y!)b<73?~^TY2DFE(2CQl(XZ04&@a({V))ST zzTrJXp)uE(Z9Hhq)auh>XwkH`83h<^Hri+uuN|X(OZ%qwa*bsgOEv5@7HiaKe$}kh zEYp0eny30oHAhunT~}R4T}yq3#x{+=Gy*i_W*}-}HIZ6^dc1m^dbGNkuBon(u7NH| z8?W82-J;!Q)MV6P^vx(wFGuf%UY6c7y?BFIgIflX2Hi$vBa#u`NKJ2=-ekRrdPW9w z40H{&436j=)Y-4IN2kZQ8w^vEj5{p}mTi_zmMIGo7u;SDv*6|e+WfBh9rN+?d*`Fi zLnK<%& zBx&YoJ=e<8da89<^O9zWW{~Dr^-A?}^-}e1)3#3AGR=RQyM`OM2d&UpuJKssq0W7s zWStMj?~L<|bB#0SJe~7oPTHJDhN*@rhKYv5dINg>dOdo4ZLT(3dr+IHeRRg*83$+V zpHZP!rdFy}tX4DY>#WLI<+HA7U)B!O4$($SW0X;((bKt4=BCZPKUZMNFF!eXw zWVqhY+fbq@(&TG$HA%Dav)gC4%x;?fPVco|uHH+%Q@Y1>kLe!P-DR}HXe;>Uu9#dh z2{s8d(KpsJ)-s-DJkxl>9K|_f<|xbw*ALYX(ht->W_H+Yzu6wM@OdHgF3vkQ@AN#P zRl8NQRh?Cnd7XK+d4>6D(-WpgO%Iwbnr}6K{`|S~!wfyN;{Q3Z3OTpLGg!-s`;4S*`D`@2bC2U(sl^k(?1?$eTYr ze?a;@tz6`^$YGJ~BEq89MU9JU7p-?#>)`IN(qWkc&t=G^&!yYtol~w;wo|&3lY_m3 zjl%+mDC_IiSFJBu2U%ZSe0K4v#m5#aSRxi83!Vkvl5II)*=tF&oMWqPJIhwpcAlMy zoxYv6-QQM+toB*$vRY-k!q(B&-qyzUf$crpJGODQur^g(IBMYst9Mp;RxhkFt(t6V zZNA!++YoG9Z5wQBY~>b;t$0?$Rs&YAO><1MO`n4L`#VytdjMOa<73bkrB zsW+)L`D)Swx=vksoq9o9fm&y^&S;&|GSM^8o1>?r_rdg?Dg66!3q~!#ED%}pEXSBB zm?3k8b4OVt3k3@~3(s1fvi#ffu;qTs7`sTjt9D^__brkv5-egZ^esRYWHG~Hn#E0% z2$QQO;U?O~vyIh_RgJHkUo#Ik4>1okzdA2$UhurYd2dW}O`n5(OgBxkO0bFrduVUE z$aJBprRjR}b>^PtuIA3>hs^ew?K0bDCN$t03>yp@?A6_+yIpsyuD|Yi&9jB>)(A1S9P*D2R3e^aheu2$Zw zxL0wH;%>#>@jc@il@yc<^o{FA|9*XXY`;_)6?N!>Nv|EV-lLW>Nk2M}=G|q6G0pK&%8}J(IFnWoU z{b;|jo5uQ%-8i;gu?=Vi){plAyvK_ag^B`2z9LUiceL(k9pH;%kzyh6Sy5%oq%jkL z1*0ti3t+)0ODWs2i^kdj)?;sux*?^YsGumXD5r=~e5#b8^hD{glK=S4z(Hw=5QwR);cgA~1=k!fRkRvYXh=?9<%S+%v#G+-_gmO~wedsapM5>S54=}j{ z+(W~MhmQ;&9X>YP3WIapb}k6?vt!t?>^SyR&Q#7c&UB6{M|DVTXvWaYAu6XEpmFFN z8k^2$uzSFetTJm7ONBL=l{uU>oIU(}_{Fdje>vY7Siv6+i~+{-UvOS>ayYphF$hf} zTnrDxd&GUreZox#7PIZx_Ut8W2ljbZAnPJ4h^5X^=gi`0aAtFA!I*HvVAEjppfSip zf%z_GoMv_lyA^<$E9%@?z$MNlP8cVgbD0w&?PG@xhYx45vRThrFIc--dsur}`&kEA zl3`>R!^W`X*e+aGt{Zn1*PUy{S;(>G*l-qcR&rc8t{gY%C!?R;Hrzgp1HFF2urhxl z7+q5VvUu6RbKVOc9}w__JXjK-kAjQBAgN%X0x(L7%%kwS04i^x5V8iM#nIvzp#8gi z2?R5UrXw=w2+wNCYRT*onvzUJ#tDNW$;nKTM@^KUGI}xqua_00oTZkJ>X(Q50Bt#$ zJUQqUgprYT#k&xh97N^sfWlz*#kp(CSIVImI5$bxvng%X)n5}Bjo zV^V*M*GSe#)=C|e$Q+WaLLhfw*LTkmnPnK6wHTR3H_lPxW-^)E+aBJ5|pk) zR|3lcS`xmAWt0osCtJ7tk=4X%X0@xR6C-VVJR${#8i`Z(0X>E-ls`Z-L_0LWMz&r#w` z;3#uq_%ZxgejGm@`1yE7v&OO%S>sqrtkVAS{>uKZ{Wbly{incw{lhxTI?rnAZ|QIA z$AMT7sed1+^$)TRv5v4_4ZR+E13vRT_{4Yp`TZaIKlK;(|JAdjXIIajp8Y)sdrtS9 z?K$5Q*b~$f(sKrkyIx>iWCSyI^zG`~)3>kh02qz6V%RWj8TJeZ#wS`4t%O!atE7FU zeWre)mQc&7Rn!{lLBqWfa|;`{D^ScQE5sQxkiiv8pI>zED9MrJd!mD$d0Vze^a z83YE2(a9LYP-G}Elo=|F<+PPFH<~+bHEkU&nQ|ZG;XI~fP_ii7={rHJXFvTQeFA+V zeKLI-U6nqQo(Cdt1t2E<1!TbCcI~4cq#mLEO+86HLoK8g(@JR-v?`h%V+q5Nv7E7j;mSy*r_mqN zGw7N0Y`R}pK-XVgJGypv?dy6;$|JoaeIR`%6_cien2{zyhoDb*Mtn}pA-*QQCw?TR zkRFnrke-sVNiRuJAm(_dExGMM+vB!##EZmG;$owGZ2I_Gp6b) zXIpn$PuoD-aGMj(1?P@i1M-c0anssow9jtWY1eNzY2QiOLplf|$R|jrNvb4uk|xL= z()-XuknZj%y8@NCz0G=Z2o9%OHIb=2VM zam~1P90^Ck9V8wlo*f;Vg01I6Isp&Kc*5^Te&g zZNS+O?TL;=XQC_7L;5{MgS>&e;C$b}#o%t^54RDkx_=os(X*tNlzr%mTf5DgGtMK3O4fqUF z7ARw|JbomdCj=3~2v-T$2~mVt!W4okK^>G$J%SNoE@3{wim-^_KyV_gBd;fKBKwp7 zBJU*cA|33-Hfgb#!w!ZGqm@;~JBwp_PCK83R-TgV0Z45x4{q0Ye-`oK4ZD%%K=jOey9RI;kIIVsJxk=#n}}P8+lhOK2Z%?&H`7RL1?eLlAa9UCyiG}@q)4y#r<81p7i9y*59D@i z1Ls!KEl(RwQ=}=;CW7c}Q1_+o@b0VK*Sl|aYl5h!KFt`!N9WNjX$yO8dKdRD>0R2p ztXGbyz#PpSE3Ks`GS~Kb^{wyo?epsk=sV6l#XQ42%RJ8vWCk&f2j&jU8<;;}Ibbz# zjP*C`1PiTq&#};U^dgQeXR)-6ZqAv{vEW#8tT;DD+F!6o5zj_gm|^+hQNyE$#|)=P z??-8@$EC%|c%$bOL!OFrJUL>0sY#r(hSbTd+s4SFlfD05bTE zc_z}nS*9RMkPSQst%DZ=il_^q0ItUyB4g+wDKep*0 zSSoM?mI<5$%LP;}4PbD4xV_vyE}UL~@G$~8fxJLLu!pyow~x1)M8 zzwvANb^LmM13yWa4BQ)O?FNVg#9O4cinofg-wUBF>lZWxbN4D9yp0z*Xi>;;Miao&(2`vy8Kx z<1FpHuH$*}ym{++8+aRezPwE!{@==L>Y_NYoU@3YUwPi=0I(fR!Q_;GrN*@CbM;cp^v__zMDnErP9rzXaO^ z+eg;h1UmqDrPT`SfO=uW$a%9AS_&5ktwwslFkj#k@EIrsoFve4DQAhZq*tm>A~RP4 znMuu)*dWlN@7j@HH6ty*Kr@|xETa)r41%&iED+0|$fC}&Udv2G{TJ3C#{%64bh$vX zKw>3@_NyjJCQ2qrsYoDIMC~UtkXe#h5)CP|U*8~Zlxh+~&C-mFDsh$gt5mhPT6|1` z`db1Wm-LJK#Z0LIG4%6K;m>zV_Kesofvm&}0c*epSOh2nE@5PDVSW~ciNb6gu>m7f zgnqA*>h^}RIBwYXaNu>m;8u3*Ei2(74@56$U2T~6uP?{9X_u)wAt-!e} zyb*kf1~U6QO=;c_Tn3E@Mt%m;_5J`P^8U*Q&pVzLsSZ{=WY8O#S>yc7^=fMrcQ_BVZcjFK!?x0`gx5k^l0iatGhb zmXeK={=EKonf&q2&-?%D^FM#r@ZCR^B-wlawbz3PeZ=qQ6TbdaX_LJt+w#BHIv5#k z%)cpM{+j{j&tMtU{BAbpcfZ%3zrG%e`Coo)_fO9>r1RzeYx$!N|I;%i%>VYMQvc(< zpIfl4$iIC;z!aE2Dny`X-{VBGcu^MeGcd{P7oU({l^}msipZd4`33U3mx#=d*5Cu= zR}T@CKY|KCP-q6{7G&#pj}hp}FVc};;gJ8kSBT7yet_)RYXtS94wprR;rh~dVMrJf zCl!zUa2xqS7OB~RKs!gsA||^LXwP?lBPdybOL06 z0(1dXm@F)L2fPQ+80H7b2kHL_fYFf8l0u*e_#!C=l#z+RBtQk23`~)licCeO0n-6h z0QQ*B)&Oh?z%Hu*5CUj>0=6VzBMF0G5IH~|0l{5>4RA)<5r6tSG5kaDcR7+=$txgF z@)~#}c?-Z}kVRo-M*w3vaR1RGL?g7apP@(hv@srw& zK=7LS`Fes^(_d2C5D2!{&^T_ObcRYGa#0FxQ=#n|*|mUP3FtKdTZspx4k8DULsEwk z2*%=$N*$B_KZu{pH7wOm(*Ft}E4i{#3;)xE8z74;dLSN%C$Jh=BefP;i>w1z3A{qk zs{>vc@Txe4oR)&~2GBf>v&h+zY;m+b`r`^(w5d(b^dwmY1esAM} zX@37xAM(3?*|GH$2%#rwc4bojkpfhV^%YXL}W$(z^cmH1h x+VlMVl>qr)eza@;{7jZ}^1s$%`qD$R`yp&Kp~ z^-1F#0b+&^?~!djl}Lah%Xi-SRd8Jo{?;3o-h)_@@SoEI=)F?aM7l^>yqk02c9S24QiaO4jwr%c}IEn=$@Iu|j z{A?dLBatx*q%rq|JS?4a|Lt#kE6DC0Rls21az!T6sTB=BB|cf0=hn7W5Tj2YYh_UTYzD|O1qCGKoBgnDQ^!7 zlxOgcU=(L3SI*wW5@3SY9PjRFnyg5Rw>yp@`_0$geX&9y2t^gk=3+ldi57KzAFG}Z zt#+FP$a-;l>dnP*u|mK!5AdGgZ(`akjHzkN;Zof2D7M=ex~}UY`H(K9HEdOE2BrL~ z=3J{pA`;v$; r5yj*z64LN`uAO!n*j9V(G7kR$?WdIAZVN{t00000NkvXXu0mjfzF)6> literal 0 HcmV?d00001 diff --git a/examples/starting-3d-shootemup/assets/Flat dark joystick border.png b/examples/starting-3d-shootemup/assets/Flat dark joystick border.png index 5ddd717e42b566f78f6441a2a92756a1e612dc74..b4e83e112eb9375cefbb86a6d044a972f8bf4dcf 100644 GIT binary patch literal 3751 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 delta 1615 zcmV-V2C(_39pDU*B!3BTNLh0L01FZT01FZU(%pXi000IONkl$95AT zsS59mS44;|gnx(N+J#8cGO*RzEFP~zospvV09F@@5#H?{@q=XL22*Ds#!`x*5VwTo z4s|@d9%0$U>nUU)7Q7)|FM+)wU6p^Om0hR{xm+$Q0v6(ikQ?aY<#~Q6%d$d$CM1W~ zyWPhizDPv)k<3iubUOVV^8QI7_f&ihfrsPx+j*f+tbbe*UkoCwg&3a*Lr*ruVzFoy zUqm9@#3sJr>w85}TrEIx+~)>gFGPHR2>TfQ+$co2A>jZL6U2uIsq=v;-uI97Tf#!c zjkg1?`t4wW(l~eGoe1k|FBqpvXt>pz0R}xt%4c&^K9fwX4@^h5 zhA#J6GvxxN40aI`y1hS ze!&%-b_4%zQ{cc6n4Tf#%mZu^VFU=K0%QaW3=gfpBS1iakgx!G5nx&i9P-2F^Eody za({+iKu&O?c?w*XhG)!&Cg zfawTOXzfh}xLhv3JP-m*OMpTeFcIK);zNMx39zpSvp$9g3nr zfC#WcT9783hTwA|M1W}tu#fn9Cn4X75Pt!tBf!4ms|47$7kny01ek^ZhY;Uj!w8>6 zhyddk;1J>)d>Td%qz4@0!NA)Q`a##Y5+IinffdyyMU3Qw=}11;@`9lUeB)Rq*dEK| zW_&OlZvDyC9AGz&&+VvSn1Fn6JsVgGaLWaiV4DGRMK73Uz@Lqm0wxIvi3pe`Ab)&O z4Vd(R`Km7PX8*^R)qv^#k9r_D-2d(P!fNoN{&CV;a6gbQJqatw?@HG=xAlWdCg|wAjV`RfH>hw#SQIrNJ#pg@LH)z6u$;Y3pt1%-8PLq%Cl9A&N?sF9;)t-u>03mkWhy=6?j?_NzDg8eR}?-TU?Se(VLoY37R;1oO<7?g%*V zdUWWH@ZG{NT+g?wF|Z1wmtB|_gj>-zEpuT^O@q_WODG2O&`T%==UFe4)8M4t)~7(t z;3}uwOl>lm+|wnY*K2C(`>`l#B^`1+nNP;R0`)#0M7U*vm~t^S`o$ERO@FN(-J(;1 z_z)o^D87>#A>lauQ*FPqkd4HIgaZ*my5d8Ghz}70;``jT3$b^+TCEOcSzd+yZiu~I zv#}}$7~0A6{AvLfMG>0U`|xJ4fLh_%q035G*N9Jlml|If9m<>K|aWyu_P!t1d&3|x5Z z^#%&djQe*yv*GR7eBdP;T6rzP(=RLGcKmF4OKAR@U`TkaUxkVd>k&09nX(WY;h_fm z(7aCR2!JNd+2eUyzL8gq@wyPgY+$C*BJCAJ7NfknL`Nwlg2YAF$w>KrNUF|acY0Pb z+4w{CTkRp=E%$PDBDE&8BFg(ZY;T0bA^K$5`q@vPQ+=-f4MOTa`w!ht=DUf6Hmd*t N002ovPDHLkV1i6p4Cnv= diff --git a/examples/starting-3d-shootemup/assets/Flat dark joystick thumb.png b/examples/starting-3d-shootemup/assets/Flat dark joystick thumb.png index 36f5262a9055b15bf36ba5ddee957e1fa854561e..8acf179f8660be36ccea1198105da35c07154fd7 100644 GIT binary patch delta 2913 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 LNkvXXu0mjflp>}T delta 1022 zcmV zyp+Nh;58IdxZlj$Jks-6fY(q+p8T89>-ApBhkP54$Db&`{}j15xktIz)J8ls@b?s9 z9{9mv@Ykm^RWMB-@(kvH@AvzEBx@c2^>wO{Y^$C3K^KuW9oai^UE2XIUX&sFaK*sK=W{nW8pTH)DjRkD8u6f%9&P z_H?`!95P40;#H&q_SjPoLek}gJY{|;sVM+6CpkADuH4L&VMeSxd7EYkV44!m zkn%)5PST5_fSl7*(dLKI8AmKX*IAg4U?|_FW7UELn>~)y{pLA;vnMUn0BaF zK-+>t?5wRI$GIa~LOZxrjiXRS`Bb;!0SM0v#nEKNbw3DuFKT*v91fH&bF6*2UngIGpYBc}= delta 858 zcmV-g1Eu_}1Lp>RiBL{Q4GJ0x0000DNk~Le0000$0000$2nGNE0IF$m-T(jrgK0xU zP)S2WAaHVTW@&6?001bFeUUv#!$2IxUsI(j6^C{ZamY}etcZ#@N)?M>p|llRbuhW~ z3z{?}DK3tJYr(;f#j1mgv#t)Vf*|+-;x0HTx=4xtOA0N2VmvtR$GdxvyLW)ls4&&+ zngCSIGE(uFn9Z$$d%UY}?Hf!2b+fX>EVC z0nC1qUT@*Z%x0}MXtk|8;gpQcbVC;;zg^i4S+bPM#YxxKaaaryvc zsH^20;NTD#EmHQn$Gdwvd;9lHtG^#oh;o`>d{*<3Asl}$BLDy{BLR4&KXw2B00(qQ zO+^Rk2@?P^60GW9(*OVf8FWQhbVF}#ZDnqB07G(RVRU6=Aa`kWXdp*PO;A^X4i^9b z0YFJaK~#9!<4p)fSLH|0cPW;2C#si7QhOAN&riE^#IoJssVp2;?)9J#j6Ccj291J9WNTd170kE z7raOSPx#>hyy1ri@Q5E4z$<=80MGdD0N(Lk0T}Sj0a)-&0hsXB0od?W0T}Vc0a)=x z0hsZR0od`k0V3cp14P094G;-`3J?wd6Cfh~8z3qk0G7yj09Yd90bq%YFM5TU@s9!6 k@wXlUBH%9rM8N~V2QQ2ab2?$h;s5{u07*qoM6N<$f{ER5$^ZZW diff --git a/examples/starting-3d-shootemup/assets/StartingTiledGround.png b/examples/starting-3d-shootemup/assets/StartingTiledGround.png index 08fb856af5bce51a979df5406f58ffce2a434a2a..380baea6566db151e41f861a43578cc7cbc510e4 100644 GIT binary patch literal 363 zcmeAS@N?(olHy`uVBq!ia0vp^4j|0I1|(Ny7TyC=Y)RhkE)4%caKYZ?lYt_f1s;*b z3=De8Ak0{?)V>TT$X?><>&pI$T~ttxsnfmA7bqlI;u=xnoS&PUnpeW$T$GwvlA5AW zo>`Ki;O^-g5Z=fq4pem6)5S5Q;?~<+zFdb4M2>tsnLVK{U{}GzzwgyN9FGRARhsqu zny^aupXW@%b+fmhXn9=o^~Mao-=3DmR`;h&@}A`NZFb~^8{1FK&3qvu9sKg&gLmt? zl2_RPO~wbVOtOd<>vdkf?{2E@y|{ec@4jMAH~*bC&y3vu=F{uPE2qRP{p~yHjMF#v zCHwtfPWtc}*5j~)%+YxJC0e?CWdeaG0_N&;baChf9aW0Z$ZZXi_0jv=q{i#n)xG#ZRKbVj zjjtX&TC=)&wE{<7!wzo0F1?^f|0g+hOYryo{`l_R<9D(Tazkb${W#9(YWg+AMlGdy zwbKJpTM0Lgx-(s8E(pCm$`f<^x~)yk-tJF2-;3+}Cd@IHcp%vEe8#fbN2HHjp1tf? z<$Q}XUWDC7hOjdifNcU$(6*udHzdidj*}vRp+MDf{zqWRc z9fRbnte+k)2`u*>=2^U%UOEGhMUrs?@jV_u`e6woS>i zzxegW_ZvZ6K;^snGw#=bw_8yAoe{{m zpA0nV_d8~zkKr5C9U3?USUMOSiJ^L=7yPdHwwvvR-8`U4Z+G(p8Gq$~CfzERpU}C# ziPWF~xy97s`^PsB&jA_VZR|A6^>bP0l+XkK=p&wV diff --git a/examples/starting-3d-shootemup/assets/starting_bulletfire.wav b/examples/starting-3d-shootemup/assets/starting_bulletfire.wav new file mode 100644 index 0000000000000000000000000000000000000000..f7045dd18c0e6f5125fccbccfc36f5023656a915 GIT binary patch literal 6148 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 edhy=ZUaI$;!{ literal 0 HcmV?d00001 diff --git a/examples/starting-3d-shootemup/starting-3d-shootemup.json b/examples/starting-3d-shootemup/starting-3d-shootemup.json index c8f0d48ab..ff5ee1145 100644 --- a/examples/starting-3d-shootemup/starting-3d-shootemup.json +++ b/examples/starting-3d-shootemup/starting-3d-shootemup.json @@ -1,7 +1,7 @@ { "firstLayout": "", "gdVersion": { - "build": 272, + "build": 274, "major": 5, "minor": 6, "revision": 0 @@ -184,6 +184,34 @@ "metadata": "", "name": "assets\\missile_red.glb", "userAdded": false + }, + { + "file": "assets/DirtParticle.png", + "kind": "image", + "metadata": "", + "name": "assets/DirtParticle.png", + "smoothed": true, + "userAdded": false + }, + { + "file": "assets/starting_bulletfire.wav", + "kind": "audio", + "metadata": "{\"localFilePath\":\"assets/starting_bulletfire.wav\",\"extension\":\".wav\",\"jfxr\":{\"data\":\"{\\\"_version\\\":1,\\\"_name\\\":\\\"Jump 1\\\",\\\"_locked\\\":[],\\\"sampleRate\\\":44100,\\\"attack\\\":0,\\\"sustain\\\":0.046066031116312134,\\\"sustainPunch\\\":100,\\\"decay\\\":0.023128080489227765,\\\"tremoloDepth\\\":0,\\\"tremoloFrequency\\\":10,\\\"frequency\\\":5188.932904132582,\\\"frequencySweep\\\":-3988.9473862897457,\\\"frequencyDeltaSweep\\\":-2833.481830076824,\\\"repeatFrequency\\\":0,\\\"frequencyJump1Onset\\\":33,\\\"frequencyJump1Amount\\\":0,\\\"frequencyJump2Onset\\\":66,\\\"frequencyJump2Amount\\\":0,\\\"harmonics\\\":0,\\\"harmonicsFalloff\\\":0.5,\\\"waveform\\\":\\\"brownnoise\\\",\\\"interpolateNoise\\\":false,\\\"vibratoDepth\\\":0,\\\"vibratoFrequency\\\":10,\\\"squareDuty\\\":50,\\\"squareDutySweep\\\":0,\\\"flangerOffset\\\":0,\\\"flangerOffsetSweep\\\":0,\\\"bitCrush\\\":16,\\\"bitCrushSweep\\\":0,\\\"lowPassCutoff\\\":22050,\\\"lowPassCutoffSweep\\\":0,\\\"highPassCutoff\\\":0,\\\"highPassCutoffSweep\\\":0,\\\"compression\\\":1,\\\"normalization\\\":true,\\\"amplification\\\":100}\",\"name\":\"assets/starting_bulletfire.wav\"}}", + "name": "assets/starting_bulletfire.wav", + "preloadAsMusic": false, + "preloadAsSound": false, + "preloadInCache": false, + "userAdded": false + }, + { + "file": "assets/Collision.wav", + "kind": "audio", + "metadata": "", + "name": "assets/Collision.wav", + "preloadAsMusic": false, + "preloadAsSound": false, + "preloadInCache": false, + "userAdded": false } ] }, @@ -547,6 +575,7 @@ { "assetStoreId": "", "name": "Player", + "persistentUuid": "eff1a501-8dc6-4230-ad3b-77713a9c2f79", "type": "Scene3D::Model3DObject", "variables": [], "effects": [], @@ -581,6 +610,30 @@ "BulletLayer": "", "RandomizedAngle": 0 }, + { + "name": "ShakeModel3D", + "type": "ShakeObject3D::ShakeModel3D", + "TranslationAmplitudeX": 0, + "TranslationAmplitudeY": 0, + "TranslationAmplitudeZ": 0, + "RotationAmplitudeX": 5, + "RotationAmplitudeY": 5, + "RotationAmplitudeZ": 5, + "Time": 0, + "Duration": 0, + "StartEasingDuration": 0, + "StopEasingDuration": 0, + "Frequency": 100, + "DeltaX": 0, + "DeltaY": 0, + "DeltaZ": 0, + "DeltaAngleX": 0, + "DeltaAngleY": 0, + "DeltaAngleZ": 0, + "NoiseTime": 0, + "Object3D": "Object3D", + "IsStartingAtCreation": false + }, { "name": "TopDownMovement", "type": "TopDownMovementBehavior::TopDownMovementBehavior", @@ -626,10 +679,12 @@ { "assetStoreId": "", "name": "Enemy", + "persistentUuid": "eeff4b83-3b20-464a-bd3e-38fdab61d2bb", "type": "Scene3D::Model3DObject", "variables": [ { "name": "Health", + "persistentUuid": "dd1b0416-3780-4c75-8842-267480c308bd", "type": "number", "value": 3 } @@ -665,6 +720,30 @@ "ExponentialCoolingRate": 0.3, "BulletLayer": "", "RandomizedAngle": 0 + }, + { + "name": "ShakeModel3D", + "type": "ShakeObject3D::ShakeModel3D", + "TranslationAmplitudeX": 5, + "TranslationAmplitudeY": 5, + "TranslationAmplitudeZ": 0, + "RotationAmplitudeX": 5, + "RotationAmplitudeY": 10, + "RotationAmplitudeZ": 5, + "Time": 0, + "Duration": 0, + "StartEasingDuration": 0, + "StopEasingDuration": 0, + "Frequency": 7, + "DeltaX": 0, + "DeltaY": 0, + "DeltaZ": 0, + "DeltaAngleX": 0, + "DeltaAngleY": 0, + "DeltaAngleZ": 0, + "NoiseTime": 0, + "Object3D": "Object3D", + "IsStartingAtCreation": false } ], "content": { @@ -688,6 +767,7 @@ { "assetStoreId": "", "name": "PlayerBullet", + "persistentUuid": "2c2e7560-3c0b-4ea4-ab89-605098c6c32c", "type": "Scene3D::Model3DObject", "variables": [], "effects": [], @@ -719,6 +799,7 @@ { "assetStoreId": "", "name": "EnemyBullet", + "persistentUuid": "2088703f-2719-4893-bf56-116cb3b0d050", "type": "Scene3D::Model3DObject", "variables": [], "effects": [], @@ -732,20 +813,182 @@ "content": { "centerLocation": "ModelOrigin", "crossfadeDuration": 0.10000000149011612, - "depth": 5.14897379705328, - "height": 5.57319141561976, + "depth": 10, + "height": 20, "isCastingShadow": true, "isReceivingShadow": true, - "keepAspectRatio": true, + "keepAspectRatio": false, "materialType": "StandardWithoutMetalness", "modelResourceName": "assets\\missile_red.glb", "originLocation": "ModelOrigin", "rotationX": 0, "rotationY": 0, "rotationZ": 0, - "width": 33.45138142032624, + "width": 40, "animations": [] } + }, + { + "assetStoreId": "", + "name": "DebrisParticle", + "persistentUuid": "6fc7cc03-9d05-4091-9bec-f693ec315dca", + "type": "ParticleEmitter3D::ParticleEmitter3D", + "variant": "", + "variables": [], + "effects": [], + "behaviors": [], + "content": { + "EndColor": "255;255;255", + "StartColor": "109;109;109", + "SpayConeAngle": 360, + "EndScale": 0.5, + "Gravity": 600, + "StartSpeedMax": 250, + "StartSizeMin": 16, + "Duration": 0.01, + "Flow": 2000, + "LifespanMin": 0.075, + "LifespanMax": 0.25, + "StartSizeMax": 16, + "StartSpeedMin": 150 + }, + "childrenContent": { + "Particle": { + "adaptCollisionMaskAutomatically": true, + "updateIfNotVisible": false, + "animations": [ + { + "name": "Image", + "useMultipleDirections": false, + "directions": [ + { + "looping": false, + "timeBetweenFrames": 0.08, + "sprites": [ + { + "hasCustomCollisionMask": true, + "image": "assets/DirtParticle.png", + "points": [], + "originPoint": { + "name": "origine", + "x": 0, + "y": 0 + }, + "centerPoint": { + "automatic": true, + "name": "centre", + "x": 0, + "y": 0 + }, + "customCollisionMask": [ + [ + { + "x": 0, + "y": 0 + }, + { + "x": 32, + "y": 0 + }, + { + "x": 32, + "y": 32 + }, + { + "x": 0, + "y": 32 + } + ] + ] + } + ] + } + ] + } + ] + } + } + }, + { + "assetStoreId": "", + "name": "LargeDebrisParticle", + "persistentUuid": "1cd3acc7-aa86-49b9-85d2-d5319137fee7", + "type": "ParticleEmitter3D::ParticleEmitter3D", + "variant": "", + "variables": [], + "effects": [], + "behaviors": [], + "content": { + "EndColor": "255;255;255", + "StartColor": "109;109;109", + "SpayConeAngle": 360, + "EndScale": 1, + "Gravity": 600, + "StartSpeedMax": 500, + "StartSizeMin": 32, + "Duration": 0.01, + "Flow": 2000, + "LifespanMin": 0.075, + "LifespanMax": 0.25, + "StartSizeMax": 32, + "StartSpeedMin": 300 + }, + "childrenContent": { + "Particle": { + "adaptCollisionMaskAutomatically": true, + "updateIfNotVisible": false, + "animations": [ + { + "name": "Image", + "useMultipleDirections": false, + "directions": [ + { + "looping": false, + "timeBetweenFrames": 0.08, + "sprites": [ + { + "hasCustomCollisionMask": true, + "image": "assets/DirtParticle.png", + "points": [], + "originPoint": { + "name": "origine", + "x": 0, + "y": 0 + }, + "centerPoint": { + "automatic": true, + "name": "centre", + "x": 0, + "y": 0 + }, + "customCollisionMask": [ + [ + { + "x": 0, + "y": 0 + }, + { + "x": 32, + "y": 0 + }, + { + "x": 32, + "y": 32 + }, + { + "x": 0, + "y": 32 + } + ] + ] + } + ] + } + ] + } + ] + } + } } ], "objectsFolderStructure": { @@ -757,6 +1000,12 @@ { "objectName": "PlayerBullet" }, + { + "objectName": "DebrisParticle" + }, + { + "objectName": "LargeDebrisParticle" + }, { "objectName": "Enemy" }, @@ -966,6 +1215,66 @@ "300" ] } + ], + "events": [ + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [ + { + "type": { + "value": "FireBullet::FireBullet::HasJustFired" + }, + "parameters": [ + "Player", + "FireBullet", + "" + ] + } + ], + "actions": [ + { + "type": { + "value": "PlaySound" + }, + "parameters": [ + "", + "assets/starting_bulletfire.wav", + "", + "20", + "RandomWithStep(0.9,1.2,0.05)" + ] + } + ] + }, + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [ + { + "type": { + "value": "FireBullet::FireBullet::HasJustFired" + }, + "parameters": [ + "Enemy", + "FireBullet", + "" + ] + } + ], + "actions": [ + { + "type": { + "value": "PlaySound" + }, + "parameters": [ + "", + "assets/starting_bulletfire.wav", + "", + "65", + "RandomWithStep(0.8,0.9,0.05)" + ] + } + ] + } ] }, { @@ -996,6 +1305,43 @@ "1" ] }, + { + "type": { + "value": "Create" + }, + "parameters": [ + "", + "DebrisParticle", + "PlayerBullet.BoundingBoxRight()", + "PlayerBullet.CenterY()", + "" + ] + }, + { + "type": { + "value": "ShakeObject3D::ShakeModel3D::Shake" + }, + "parameters": [ + "Enemy", + "ShakeModel3D", + "0.25", + "0", + "0.2", + "" + ] + }, + { + "type": { + "value": "PlaySound" + }, + "parameters": [ + "", + "assets/Collision.wav", + "", + "30", + "RandomWithStep(0.9,1.1,0.05)" + ] + }, { "type": { "value": "Delete" @@ -1031,6 +1377,18 @@ "Enemy", "" ] + }, + { + "type": { + "value": "Create" + }, + "parameters": [ + "", + "LargeDebrisParticle", + "Enemy.CenterX()", + "Enemy.CenterY()", + "" + ] } ] }, @@ -1048,9 +1406,77 @@ "", "" ] + }, + { + "type": { + "value": "BuiltinCommonInstructions::Once" + }, + "parameters": [] } ], "actions": [ + { + "type": { + "value": "ShakeObject3D::ShakeModel3D::Shake" + }, + "parameters": [ + "Player", + "ShakeModel3D", + "2", + "0", + "0", + "" + ] + }, + { + "type": { + "value": "PlaySound" + }, + "parameters": [ + "", + "assets/Collision.wav", + "", + "90", + "RandomFloatInRange(0.7,0.9)" + ] + }, + { + "type": { + "value": "Create" + }, + "parameters": [ + "", + "LargeDebrisParticle", + "EnemyBullet.CenterX()", + "EnemyBullet.CenterY()", + "" + ] + }, + { + "type": { + "value": "ChangeTimeScale" + }, + "parameters": [ + "", + "0.15" + ] + }, + { + "type": { + "value": "ClearForces" + }, + "parameters": [ + "EnemyBullet" + ] + }, + { + "type": { + "value": "Wait" + }, + "parameters": [ + "0.15" + ] + }, { "type": { "value": "Scene" @@ -1182,6 +1608,11 @@ "name": "Scale", "type": "ScalableCapability::ScalableBehavior" }, + { + "name": "ShakeModel3D", + "type": "ShakeObject3D::ShakeModel3D", + "EasingFactor": 0 + }, { "name": "TopDownMovement", "type": "TopDownMovementBehavior::TopDownMovementBehavior" @@ -14807,6 +15238,14107 @@ }, "eventsBasedBehaviors": [], "eventsBasedObjects": [] + }, + { + "author": "", + "category": "Visual effect", + "dimension": "3D", + "extensionNamespace": "", + "fullName": "3D particle emitter", + "gdevelopVersion": ">=5.5.222", + "helpPath": "", + "iconUrl": "data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iVVRGLTgiPz48IURPQ1RZUEUgc3ZnIFBVQkxJQyAiLS8vVzNDLy9EVEQgU1ZHIDEuMS8vRU4iICJodHRwOi8vd3d3LnczLm9yZy9HcmFwaGljcy9TVkcvMS4xL0RURC9zdmcxMS5kdGQiPjxzdmcgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIiB4bWxuczp4bGluaz0iaHR0cDovL3d3dy53My5vcmcvMTk5OS94bGluayIgdmVyc2lvbj0iMS4xIiBpZD0ibWRpLWZpcmUiIHdpZHRoPSIyNCIgaGVpZ2h0PSIyNCIgdmlld0JveD0iMCAwIDI0IDI0Ij48cGF0aCBkPSJNMTcuNjYgMTEuMkMxNy40MyAxMC45IDE3LjE1IDEwLjY0IDE2Ljg5IDEwLjM4QzE2LjIyIDkuNzggMTUuNDYgOS4zNSAxNC44MiA4LjcyQzEzLjMzIDcuMjYgMTMgNC44NSAxMy45NSAzQzEzIDMuMjMgMTIuMTcgMy43NSAxMS40NiA0LjMyQzguODcgNi40IDcuODUgMTAuMDcgOS4wNyAxMy4yMkM5LjExIDEzLjMyIDkuMTUgMTMuNDIgOS4xNSAxMy41NUM5LjE1IDEzLjc3IDkgMTMuOTcgOC44IDE0LjA1QzguNTcgMTQuMTUgOC4zMyAxNC4wOSA4LjE0IDEzLjkzQzguMDggMTMuODggOC4wNCAxMy44MyA4IDEzLjc2QzYuODcgMTIuMzMgNi42OSAxMC4yOCA3LjQ1IDguNjRDNS43OCAxMCA0Ljg3IDEyLjMgNSAxNC40N0M1LjA2IDE0Ljk3IDUuMTIgMTUuNDcgNS4yOSAxNS45N0M1LjQzIDE2LjU3IDUuNyAxNy4xNyA2IDE3LjdDNy4wOCAxOS40MyA4Ljk1IDIwLjY3IDEwLjk2IDIwLjkyQzEzLjEgMjEuMTkgMTUuMzkgMjAuOCAxNy4wMyAxOS4zMkMxOC44NiAxNy42NiAxOS41IDE1IDE4LjU2IDEyLjcyTDE4LjQzIDEyLjQ2QzE4LjIyIDEyIDE3LjY2IDExLjIgMTcuNjYgMTEuMk0xNC41IDE3LjVDMTQuMjIgMTcuNzQgMTMuNzYgMTggMTMuNCAxOC4xQzEyLjI4IDE4LjUgMTEuMTYgMTcuOTQgMTAuNSAxNy4yOEMxMS42OSAxNyAxMi40IDE2LjEyIDEyLjYxIDE1LjIzQzEyLjc4IDE0LjQzIDEyLjQ2IDEzLjc3IDEyLjMzIDEzQzEyLjIxIDEyLjI2IDEyLjIzIDExLjYzIDEyLjUgMTAuOTRDMTIuNjkgMTEuMzIgMTIuODkgMTEuNyAxMy4xMyAxMkMxMy45IDEzIDE1LjExIDEzLjQ0IDE1LjM3IDE0LjhDMTUuNDEgMTQuOTQgMTUuNDMgMTUuMDggMTUuNDMgMTUuMjNDMTUuNDYgMTYuMDUgMTUuMSAxNi45NSAxNC41IDE3LjVIMTQuNVoiIC8+PC9zdmc+", + "name": "ParticleEmitter3D", + "previewIconUrl": "https://asset-resources.gdevelop.io/public-resources/Icons/f2e5a34bf465f781866677762d385d6c8e9e8d203383f2df9a3b7e0fad6a2cb5_fire.svg", + "shortDescription": "Display a large number of particles in 3D to create visual effects in a 3D game.", + "version": "3.1.0", + "description": [ + "3D particle emitters let you create and display many small particles to simulate visual effects in your game — like fire, explosions, smoke, or dust.", + "", + "The parameters of the object can be configured in multiple different ways to create a specific desired effect. GDevelop will give a set of a pre-configured 3D particle emitters that you should try first.", + "", + "Use these 3D particle emitters in 3D games. For 2D games or particles that appear on a game's 2D user interface, use the 2D particle emitter object instead." + ], + "origin": { + "identifier": "ParticleEmitter3D", + "name": "gdevelop-extension-store" + }, + "tags": [ + "3d", + "particle", + "explosion", + "fire", + "smoke", + "splash", + "light" + ], + "authorIds": [ + "IWykYNRvhCZBN3vEgKEbBPOR3Oc2" + ], + "changelog": [ + { + "version": "3.0.0", + "breaking": "- the object rotates the other way around X axis." + }, + { + "version": "2.0.0", + "breaking": "- Object properties for position and rotation have been removed. They must be set with the instance editor or the action." + }, + { + "version": "1.0.0", + "breaking": "- Particles were 3 times too small." + } + ], + "dependencies": [], + "globalVariables": [], + "sceneVariables": [], + "eventsFunctions": [ + { + "description": "Define helper classes JavaScript code.", + "fullName": "Define helper classes", + "functionType": "Action", + "name": "DefineHelperClasses", + "private": true, + "sentence": "Define helper classes JavaScript code", + "events": [ + { + "type": "BuiltinCommonInstructions::JsCode", + "inlineCode": [ + "if (gdjs.__particleEmmiter3DExtension) {", + " return;", + "}", + "", + "const game = runtimeScene.getGame();", + "", + "class ParticleEmitter3DRenderer extends gdjs.CustomRuntimeObject3DRenderer {", + " constructor(", + " object,", + " instanceContainer,", + " parent", + " ) {", + " super(object, instanceContainer, parent);", + " }", + "", + " _updateThreeGroup() {", + " const threeObject3D = this.get3DRendererObject();", + "", + " threeObject3D.rotation.set(", + " gdjs.toRad(this._object.getRotationX()),", + " gdjs.toRad(this._object.getRotationY()),", + " gdjs.toRad(this._object.angle)", + " );", + "", + " threeObject3D.position.set(", + " this._object.getX(),", + " this._object.getY(),", + " this._object.getZ()", + " );", + "", + " // Force the scale to 1 because the particle emitter doesn't really has a size.", + " threeObject3D.scale.set(", + " this._object.isFlippedX() ? -1 : 1,", + " this._object.isFlippedY() ? -1 : 1,", + " this._object.isFlippedZ() ? -1 : 1", + " );", + "", + " threeObject3D.visible = !this._object.hidden;", + "", + " this._isContainerDirty = true;", + " }", + "}", + "", + "const coneLength = 64;", + "", + "class ParticleEmitterHelper extends THREE.Object3D {", + " /** @type {gdjs.CustomRuntimeObject3D} */", + " object;", + " /** @type {THREE.LineSegments} */", + " cone;", + " /** @type {THREE.LineSegments} */", + " centerLine;", + " /** @type {THREE.Mesh} */", + " originBox;", + "", + "\t/**", + " * @param gdjs.CustomRuntimeObject3D object", + "\t */", + " constructor(object) {", + " super();", + " this.object = object;", + " this.type = 'ParticleEmitterHelper';", + "", + " const coneGeometry = new THREE.BufferGeometry();", + " const conePositions = [", + " 0, 0, 0, 0, 0, 1,", + " 0, 0, 0, 1, 0, 1,", + " 0, 0, 0, - 1, 0, 1,", + " 0, 0, 0, 0, 1, 1,", + " 0, 0, 0, 0, - 1, 1", + " ];", + " for (let i = 0, j = 1, l = 32; i < l; i++ , j++) {", + " const p1 = (i / l) * Math.PI * 2;", + " const p2 = (j / l) * Math.PI * 2;", + " conePositions.push(", + " Math.cos(p1), Math.sin(p1), 1,", + " Math.cos(p2), Math.sin(p2), 1", + " );", + " }", + " coneGeometry.setAttribute('position', new THREE.Float32BufferAttribute(conePositions, 3));", + " this.cone = new THREE.LineSegments(", + " coneGeometry,", + " new THREE.LineBasicMaterial({ fog: false, toneMapped: false })", + " );", + " this.add(this.cone);", + " const centerLineGeometry = new THREE.BufferGeometry();", + " centerLineGeometry.setAttribute('position', new THREE.Float32BufferAttribute([", + " 0, 0, 0, 0, 0, coneLength", + " ], 3));", + " this.centerLine = new THREE.LineSegments(", + " centerLineGeometry,", + " new THREE.LineBasicMaterial({ fog: false, toneMapped: false })", + " );", + " this.add(this.centerLine);", + "", + " this.originBox = new THREE.Mesh(", + " new THREE.BoxGeometry(16, 16, 16),", + " new THREE.MeshBasicMaterial({ fog: false, toneMapped: false })", + " );", + " this.add(this.originBox);", + " this.update();", + "", + " this.originBox.gdjsRuntimeObject = object;", + " }", + "", + " dispose() {", + " this.cone.geometry.dispose();", + " this.cone.material.dispose();", + " this.centerLine.geometry.dispose();", + " this.centerLine.material.dispose();", + " this.originBox.geometry.dispose();", + " this.originBox.material.dispose();", + " }", + "", + " update() {", + " const coneWidth = coneLength * Math.sin(gdjs.toRad(this.object._getSpayConeAngle()));", + " const coneHeight = coneLength * Math.cos(gdjs.toRad(this.object._getSpayConeAngle()));", + " this.cone.scale.set(coneWidth, coneWidth, coneHeight);", + " const startColor = gdjs.rgbOrHexStringToNumber(this.object._getStartColor())", + " const endColor = gdjs.rgbOrHexStringToNumber(this.object._getEndColor());", + " this.cone.material.color.set(endColor);", + " this.centerLine.material.color.set(startColor);", + " this.originBox.material.color.set(startColor);", + " }", + "}", + "", + "/**", + " * @param {string} colorString", + " * @param {THREE.Vector4} threeColor", + " */", + "const setThreeColor = (colorString, threeColor = new THREE.Vector4()) => {", + " const integerColor = gdjs.rgbOrHexToRGBColor(colorString);", + " threeColor.x = integerColor[0] / 255;", + " threeColor.y = integerColor[1] / 255;", + " threeColor.z = integerColor[2] / 255;", + "};", + "", + "/**", + " * @param {string} integerOpacity", + " * @param {THREE.Vector4} threeColor", + " */", + "const setThreeOpacity = (integerOpacity, threeColor = new THREE.Vector4()) => {", + " threeColor.w = integerOpacity / 255;", + "};", + "", + "class ParticleEmitterAdapter {", + " /**", + " * @param particleSystem {ParticleSystem}", + " * @param colorOverLife {ColorOverLife}", + " * @param sizeOverLife {SizeOverLife}", + " * @param applyForce {ApplyForce}", + " * @param applyForce {ApplyForce}", + " * @param widthOverLength {WidthOverLength}", + " */", + " constructor(particleSystem, colorOverLife, sizeOverLife, applyForce, widthOverLength) {", + " this.particleSystem = particleSystem;", + " this.colorOverLife = colorOverLife;", + " this.sizeOverLife = sizeOverLife;", + " this.applyForce = applyForce;", + " this.widthOverLength = widthOverLength;", + " }", + "", + " /**", + " * @param renderMode {'Billboard' | 'Trail'}", + " */", + " setRenderMode(renderMode) {", + " switch (renderMode) {", + " case \"BillBoard\":", + " this.particleSystem.renderMode = RenderMode.BillBoard;", + " break;", + " case \"Trail\":", + " this.particleSystem.renderMode = RenderMode.Trail;", + " this.particleSystem.rendererEmitterSettings.startLength = new IntervalValue(30, 30);", + " break;", + " }", + " }", + "", + " /**", + " * @param startLengthMin {number}", + " */", + " setTrailStartLengthMin(startLengthMin) {", + " const startLength = this.particleSystem.rendererEmitterSettings.startLength;", + " if (!startLength) {", + " return;", + " }", + " startLength.a = startLengthMin;", + " }", + "", + " /**", + " * @param startLengthMax {number}", + " */", + " setTrailStartLengthMax(startLengthMax) {", + " const startLength = this.particleSystem.rendererEmitterSettings.startLength;", + " if (!startLength) {", + " return;", + " }", + " startLength.b = startLengthMax;", + " }", + "", + " /**", + " * @param followLocalOrigin {boolean}", + " */", + " setTrailFollowingLocalOrigin(followLocalOrigin) {", + " this.particleSystem.rendererEmitterSettings.followLocalOrigin = followLocalOrigin;", + " }", + "", + " /**", + " * @param trailEndWidthRatio {number}", + " */", + " setTrailEndWidthRatio(trailEndWidthRatio) {", + " // Check this comment in the lib code:", + " // We added a multiplication with size because it's more convenient for the extension.", + "", + " /** @type Bezier */", + " const bezier = this.widthOverLength.width.functions[0][0];", + " bezier.p[0] = 1;", + " bezier.p[1] = gdjs.evtTools.common.lerp(1, trailEndWidthRatio, 1 / 3);", + " bezier.p[2] = gdjs.evtTools.common.lerp(1, trailEndWidthRatio, 2 / 3);", + " bezier.p[3] = trailEndWidthRatio;", + " }", + "", + "", + " /**", + " * @param startColor {string}", + " */", + " setStartColor(startColor) {", + " setThreeColor(startColor, this.colorOverLife.color.color.keys[0][0]);", + " }", + "", + " /**", + " * @param endColor {string}", + " */", + " setEndColor(endColor) {", + " setThreeColor(endColor, this.colorOverLife.color.color.keys[1][0]);", + " }", + "", + " /**", + " * @param startOpacity {number}", + " */", + " setStartOpacity(startOpacity) {", + " this.colorOverLife.color.alpha.keys[0][0] = startOpacity / 255;", + " }", + "", + " /**", + " * @param endOpacity {number}", + " */", + " setEndOpacity(endOpacity) {", + " this.colorOverLife.color.alpha.keys[1][0] = endOpacity / 255;", + " }", + "", + " /**", + " * @param flow {number}", + " */", + " setFlow(flow) {", + " this.particleSystem.emissionOverTime.value = flow;", + " }", + "", + " /**", + " * @param startMinSize {number}", + " */", + " setStartMinSize(startMinSize) {", + " this.particleSystem.startSize.a = startMinSize;", + " }", + "", + " /**", + " * @param startMaxSize {number}", + " */", + " setStartMaxSize(startMaxSize) {", + " this.particleSystem.startSize.b = startMaxSize;", + " }", + "", + " /**", + " * @param endScale {number}", + " */", + " setEndScale(endScale) {", + " /** @type Bezier */", + " const bezier = this.sizeOverLife.size.functions[0][0];", + " bezier.p[0] = 1;", + " bezier.p[1] = gdjs.evtTools.common.lerp(1, endScale, 1 / 3);", + " bezier.p[2] = gdjs.evtTools.common.lerp(1, endScale, 2 / 3);", + " bezier.p[3] = endScale;", + " }", + "", + " /**", + " * @param startSpeedMin {number}", + " */", + " setStartSpeedMin(startSpeedMin) {", + " this.particleSystem.startSpeed.a = startSpeedMin;", + " }", + "", + " /**", + " * @param startSpeedMax {number}", + " */", + " setStartSpeedMax(startSpeedMax) {", + " this.particleSystem.startSpeed.b = startSpeedMax;", + " }", + "", + " /**", + " * @param lifespanMin {number}", + " */", + " setLifespanMin(lifespanMin) {", + " this.particleSystem.startLife.a = lifespanMin;", + " }", + "", + " /**", + " * @param lifespanMax {number}", + " */", + " setLifespanMax(lifespanMax) {", + " this.particleSystem.startLife.b = lifespanMax;", + " }", + "", + " /**", + " * @param duration {number}", + " */", + " setDuration(duration) {", + " this.particleSystem.duration = duration || Number.POSITIVE_INFINITY;", + " }", + "", + " /**", + " * @param areParticlesRelative {boolean}", + " */", + " setParticlesRelative(areParticlesRelative) {", + " this.particleSystem.worldSpace = !areParticlesRelative;", + " }", + "", + " /**", + " * @param sprayConeAngle {number}", + " */", + " setSprayConeAngle(sprayConeAngle) {", + " if (0 < sprayConeAngle && sprayConeAngle <= 180) {", + " if (!this.particleSystem.emitterShape.angle) {", + " this.particleSystem.emitterShape = new ConeEmitter({ radius: 0.1, angle: Math.PI / 8 });", + " }", + " this.particleSystem.emitterShape.angle = sprayConeAngle * Math.PI / 180;", + " }", + " else {", + " if (this.particleSystem.emitterShape.angle) {", + " this.particleSystem.emitterShape = new PointEmitter();", + " }", + " }", + " }", + "", + " /**", + " * @param blending {string}", + " */", + " setBlending(blendingString) {", + " const blending =", + " blendingString === \"Additive\" ? THREE.AdditiveBlending :", + " blendingString === \"Normal\" ? THREE.NormalBlending :", + " blendingString === \"Subtractive\" ? THREE.SubtractiveBlending :", + " blendingString === \"Multiply\" ? THREE.MultiplyBlending :", + " blendingString === \"None\" ? THREE.NoBlending :", + " THREE.AdditiveBlending;", + " // TODO This doesn't work.", + " this.particleSystem.blending = blending;", + " }", + "", + " /**", + " * @param gravity {number}", + " */", + " setGravity(gravity) {", + " this.applyForce.magnitude.value = gravity;", + " }", + "", + " /**", + " * @param gravityTop {string}", + " */", + " setGravityTop(gravityTop) {", + " // TODO Make gravity absolute even when \"relative\" is enabled. ", + " switch (gravityTop) {", + " case \"Z+\":", + " this.applyForce.direction.set(0, 0, -1);", + " break;", + "", + " case \"Y-\":", + " this.applyForce.direction.set(0, 1, 0);", + " break;", + " }", + " }", + "", + " setImage(resourceName) {", + " const texture = game.getImageManager().getThreeTexture(resourceName);", + " this.particleSystem.texture = texture;", + " }", + "}", + "", + "", + "", + "/**", + " * three.quarks v0.11.2 build Mon Jan 22 2024", + " * https://github.com/Alchemist0823/three.quarks#readme", + " * Copyright 2024 Alchemist0823 , MIT", + " */", + "", + "// The migration to addUpdateRange was backported without updating the library", + "// because GDevelop still uses Three.js 0.160.0.", + "", + "class ParticleEmitter extends THREE.Object3D {", + " constructor(system) {", + " super();", + " this.type = 'ParticleEmitter';", + " this.system = system;", + " }", + " clone() {", + " const system = this.system.clone();", + " system.emitter.copy(this, true);", + " return system.emitter;", + " }", + " dispose() { }", + " extractFromCache(cache) {", + " const values = [];", + " for (const key in cache) {", + " const data = cache[key];", + " delete data.metadata;", + " values.push(data);", + " }", + " return values;", + " }", + " toJSON(meta, options = {}) {", + " const isRootObject = meta === undefined || typeof meta === 'string';", + " const output = {};", + " if (isRootObject) {", + " meta = {", + " geometries: {},", + " materials: {},", + " textures: {},", + " images: {},", + " shapes: {},", + " skeletons: {},", + " animations: {},", + " nodes: {},", + " };", + " output.metadata = {", + " version: 4.5,", + " type: 'Object',", + " generator: 'Object3D.toJSON',", + " };", + " }", + " const object = {};", + " object.uuid = this.uuid;", + " object.type = this.type;", + " if (this.name !== '')", + " object.name = this.name;", + " if (this.castShadow === true)", + " object.castShadow = true;", + " if (this.receiveShadow === true)", + " object.receiveShadow = true;", + " if (this.visible === false)", + " object.visible = false;", + " if (this.frustumCulled === false)", + " object.frustumCulled = false;", + " if (this.renderOrder !== 0)", + " object.renderOrder = this.renderOrder;", + " if (JSON.stringify(this.userData) !== '{}')", + " object.userData = this.userData;", + " object.layers = this.layers.mask;", + " object.matrix = this.matrix.toArray();", + " if (this.matrixAutoUpdate === false)", + " object.matrixAutoUpdate = false;", + " if (this.system !== null)", + " object.ps = this.system.toJSON(meta, options);", + " if (this.children.length > 0) {", + " object.children = [];", + " for (let i = 0; i < this.children.length; i++) {", + " if (this.children[i].type !== 'ParticleSystemPreview') {", + " object.children.push(this.children[i].toJSON(meta).object);", + " }", + " }", + " }", + " if (isRootObject) {", + " const geometries = this.extractFromCache(meta.geometries);", + " const materials = this.extractFromCache(meta.materials);", + " const textures = this.extractFromCache(meta.textures);", + " const images = this.extractFromCache(meta.images);", + " if (geometries.length > 0)", + " output.geometries = geometries;", + " if (materials.length > 0)", + " output.materials = materials;", + " if (textures.length > 0)", + " output.textures = textures;", + " if (images.length > 0)", + " output.images = images;", + " }", + " output.object = object;", + " return output;", + " }", + "}", + "", + "class LinkedListNode {", + " constructor(data) {", + " this.data = data;", + " this.next = null;", + " this.prev = null;", + " }", + " hasPrev() {", + " return this.prev !== null;", + " }", + " hasNext() {", + " return this.next !== null;", + " }", + "}", + "class LinkedList {", + " constructor() {", + " this.length = 0;", + " this.head = this.tail = null;", + " }", + " isEmpty() {", + " return this.head === null;", + " }", + " clear() {", + " this.length = 0;", + " this.head = this.tail = null;", + " }", + " front() {", + " if (this.head === null)", + " return null;", + " return this.head.data;", + " }", + " back() {", + " if (this.tail === null)", + " return null;", + " return this.tail.data;", + " }", + " dequeue() {", + " if (this.head) {", + " const value = this.head.data;", + " this.head = this.head.next;", + " if (!this.head) {", + " this.tail = null;", + " }", + " else {", + " this.head.prev = null;", + " }", + " this.length--;", + " return value;", + " }", + " return undefined;", + " }", + " pop() {", + " if (this.tail) {", + " const value = this.tail.data;", + " this.tail = this.tail.prev;", + " if (!this.tail) {", + " this.head = null;", + " }", + " else {", + " this.tail.next = null;", + " }", + " this.length--;", + " return value;", + " }", + " return undefined;", + " }", + " queue(data) {", + " const node = new LinkedListNode(data);", + " if (!this.tail) {", + " this.tail = node;", + " }", + " if (this.head) {", + " this.head.prev = node;", + " node.next = this.head;", + " }", + " this.head = node;", + " this.length++;", + " }", + " push(data) {", + " const node = new LinkedListNode(data);", + " if (!this.head) {", + " this.head = node;", + " }", + " if (this.tail) {", + " this.tail.next = node;", + " node.prev = this.tail;", + " }", + " this.tail = node;", + " this.length++;", + " }", + " insertBefore(node, data) {", + " const newNode = new LinkedListNode(data);", + " newNode.next = node;", + " newNode.prev = node.prev;", + " if (newNode.prev !== null) {", + " newNode.prev.next = newNode;", + " }", + " newNode.next.prev = newNode;", + " if (node == this.head) {", + " this.head = newNode;", + " }", + " this.length++;", + " }", + " remove(data) {", + " if (this.head === null || this.tail === null) {", + " return;", + " }", + " let tempNode = this.head;", + " if (data === this.head.data) {", + " this.head = this.head.next;", + " }", + " if (data === this.tail.data) {", + " this.tail = this.tail.prev;", + " }", + " while (tempNode.next !== null && tempNode.data !== data) {", + " tempNode = tempNode.next;", + " }", + " if (tempNode.data === data) {", + " if (tempNode.prev !== null)", + " tempNode.prev.next = tempNode.next;", + " if (tempNode.next !== null)", + " tempNode.next.prev = tempNode.prev;", + " this.length--;", + " }", + " }", + " *values() {", + " let current = this.head;", + " while (current !== null) {", + " yield current.data;", + " current = current.next;", + " }", + " }", + "}", + "", + "class NodeParticle {", + " constructor() {", + " this.position = new THREE.Vector3();", + " this.velocity = new THREE.Vector3();", + " this.age = 0;", + " this.life = 1;", + " this.size = 1;", + " this.rotation = 0;", + " this.color = new THREE.Vector4(1, 1, 1, 1);", + " this.uvTile = 0;", + " }", + " get died() {", + " return this.age >= this.life;", + " }", + " reset() {", + " this.position.set(0, 0, 0);", + " this.velocity.set(0, 0, 0);", + " this.age = 0;", + " this.life = 1;", + " this.size = 1;", + " this.rotation = 0;", + " this.color.set(1, 1, 1, 1);", + " this.uvTile = 0;", + " }", + "}", + "class SpriteParticle {", + " constructor() {", + " this.startSpeed = 0;", + " this.startColor = new THREE.Vector4();", + " this.startSize = 1;", + " this.position = new THREE.Vector3();", + " this.velocity = new THREE.Vector3();", + " this.age = 0;", + " this.life = 1;", + " this.size = 1;", + " this.speedModifier = 1;", + " this.rotation = 0;", + " this.color = new THREE.Vector4();", + " this.uvTile = 0;", + " }", + " get died() {", + " return this.age >= this.life;", + " }", + "}", + "class RecordState {", + " constructor(position, size, color) {", + " this.position = position;", + " this.size = size;", + " this.color = color;", + " }", + "}", + "class TrailParticle {", + " constructor() {", + " this.startSpeed = 0;", + " this.startColor = new THREE.Vector4();", + " this.startSize = 1;", + " this.position = new THREE.Vector3();", + " this.velocity = new THREE.Vector3();", + " this.age = 0;", + " this.life = 1;", + " this.size = 1;", + " this.length = 100;", + " this.speedModifier = 1;", + " this.color = new THREE.Vector4();", + " this.previous = new LinkedList();", + " this.uvTile = 0;", + " }", + " update() {", + " if (this.age <= this.life) {", + " this.previous.push(new RecordState(this.position.clone(), this.size, this.color.clone()));", + " }", + " else {", + " if (this.previous.length > 0) {", + " this.previous.dequeue();", + " }", + " }", + " while (this.previous.length > this.length) {", + " this.previous.dequeue();", + " }", + " }", + " get died() {", + " return this.age >= this.life;", + " }", + " reset() {", + " this.previous.clear();", + " }", + "}", + "", + "class Bezier {", + " constructor(p1, p2, p3, p4) {", + " this.p = [p1, p2, p3, p4];", + " }", + " genValue(t) {", + " const t2 = t * t;", + " const t3 = t * t * t;", + " const mt = 1 - t;", + " const mt2 = mt * mt;", + " const mt3 = mt2 * mt;", + " return this.p[0] * mt3 + this.p[1] * mt2 * t * 3 + this.p[2] * mt * t2 * 3 + this.p[3] * t3;", + " }", + " derivativeCoefficients(points) {", + " const dpoints = [];", + " for (let p = points, c = p.length - 1; c > 0; c--) {", + " const list = [];", + " for (let j = 0; j < c; j++) {", + " const dpt = c * (p[j + 1] - p[j]);", + " list.push(dpt);", + " }", + " dpoints.push(list);", + " p = list;", + " }", + " return dpoints;", + " }", + " getSlope(t) {", + " const p = this.derivativeCoefficients(this.p)[0];", + " const mt = 1 - t;", + " const a = mt * mt;", + " const b = mt * t * 2;", + " const c = t * t;", + " return a * p[0] + b * p[1] + c * p[2];", + " }", + " controlCurve(d0, d1) {", + " this.p[1] = d0 / 3 + this.p[0];", + " this.p[2] = this.p[3] - d1 / 3;", + " }", + " hull(t) {", + " let p = this.p;", + " let _p = [], pt, idx = 0, i = 0, l = 0;", + " const q = [];", + " q[idx++] = p[0];", + " q[idx++] = p[1];", + " q[idx++] = p[2];", + " q[idx++] = p[3];", + " while (p.length > 1) {", + " _p = [];", + " for (i = 0, l = p.length - 1; i < l; i++) {", + " pt = t * p[i] + (1 - t) * p[i + 1];", + " q[idx++] = pt;", + " _p.push(pt);", + " }", + " p = _p;", + " }", + " return q;", + " }", + " split(t) {", + " const q = this.hull(t);", + " const result = {", + " left: new Bezier(q[0], q[4], q[7], q[9]),", + " right: new Bezier(q[9], q[8], q[6], q[3]),", + " span: q", + " };", + " return result;", + " }", + " clone() {", + " return new Bezier(this.p[0], this.p[1], this.p[2], this.p[3]);", + " }", + " toJSON() {", + " return {", + " p0: this.p[0],", + " p1: this.p[1],", + " p2: this.p[2],", + " p3: this.p[3],", + " };", + " }", + " static fromJSON(json) {", + " return new Bezier(json.p0, json.p1, json.p2, json.p3);", + " }", + "}", + "", + "const ColorToJSON = (color) => {", + " return { r: color.x, g: color.y, b: color.z, a: color.w };", + "};", + "const JSONToColor = (json) => {", + " return new THREE.Vector4(json.r, json.g, json.b, json.a);", + "};", + "const JSONToValue = (json, type) => {", + " switch (type) {", + " case 'Vector3':", + " return new THREE.Vector3(json.x, json.y, json.z);", + " case 'Vector4':", + " return new THREE.Vector4(json.x, json.y, json.z, json.w);", + " case 'Color':", + " return new THREE.Vector3(json.r, json.g, json.b);", + " case 'Number':", + " return json;", + " default:", + " return json;", + " }", + "};", + "const ValueToJSON = (value, type) => {", + " switch (type) {", + " case 'Vector3':", + " return { x: value.x, y: value.y, z: value.z };", + " case 'Vector4':", + " return { x: value.x, y: value.y, z: value.z, w: value.w };", + " case 'Color':", + " return { r: value.x, g: value.y, b: value.z };", + " case 'Number':", + " return value;", + " default:", + " return value;", + " }", + "};", + "", + "class RandomColor {", + " constructor(a, b) {", + " this.a = a;", + " this.b = b;", + " this.type = \"value\";", + " }", + " genColor(color) {", + " const rand = Math.random();", + " return color.copy(this.a).lerp(this.b, rand);", + " }", + " toJSON() {", + " return {", + " type: \"RandomColor\",", + " a: ColorToJSON(this.a),", + " b: ColorToJSON(this.b),", + " };", + " }", + " static fromJSON(json) {", + " return new RandomColor(JSONToColor(json.a), JSONToColor(json.b));", + " }", + " clone() {", + " return new RandomColor(this.a.clone(), this.b.clone());", + " }", + "}", + "", + "class ColorRange {", + " constructor(a, b) {", + " this.a = a;", + " this.b = b;", + " this.type = 'value';", + " }", + " genColor(color, t) {", + " return color.copy(this.a).lerp(this.b, Math.random());", + " }", + " toJSON() {", + " return {", + " type: 'ColorRange',", + " a: ColorToJSON(this.a),", + " b: ColorToJSON(this.b),", + " };", + " }", + " static fromJSON(json) {", + " return new ColorRange(JSONToColor(json.a), JSONToColor(json.b));", + " }", + " clone() {", + " return new ColorRange(this.a.clone(), this.b.clone());", + " }", + "}", + "", + "class ContinuousLinearFunction {", + " constructor(keys, subType) {", + " this.subType = subType;", + " this.type = 'function';", + " this.keys = keys;", + " }", + " findKey(t) {", + " let mid = 0;", + " let left = 0, right = this.keys.length - 1;", + " while (left + 1 < right) {", + " mid = Math.floor((left + right) / 2);", + " if (t < this.getStartX(mid))", + " right = mid - 1;", + " else if (t > this.getEndX(mid))", + " left = mid + 1;", + " else", + " return mid;", + " }", + " for (let i = left; i <= right; i++) {", + " if (t >= this.getStartX(i) && t <= this.getEndX(i))", + " return i;", + " }", + " return -1;", + " }", + " getStartX(index) {", + " return this.keys[index][1];", + " }", + " getEndX(index) {", + " if (index + 1 < this.keys.length)", + " return this.keys[index + 1][1];", + " return 1;", + " }", + " genValue(value, t) {", + " const index = this.findKey(t);", + " if (this.subType === 'Number') {", + " if (index === -1) {", + " return this.keys[0][0];", + " }", + " else if (index + 1 >= this.keys.length) {", + " return this.keys[this.keys.length - 1][0];", + " }", + " return ((this.keys[index + 1][0] - this.keys[index][0]) *", + " ((t - this.getStartX(index)) / (this.getEndX(index) - this.getStartX(index))) +", + " this.keys[index][0]);", + " }", + " else {", + " if (index === -1) {", + " return value.copy(this.keys[0][0]);", + " }", + " if (index + 1 >= this.keys.length) {", + " return value.copy(this.keys[this.keys.length - 1][0]);", + " }", + " return value", + " .copy(this.keys[index][0])", + " .lerp(this.keys[index + 1][0], (t - this.getStartX(index)) / (this.getEndX(index) - this.getStartX(index)));", + " }", + " }", + " toJSON() {", + " this.keys[0][0].constructor.name;", + " return {", + " type: 'CLinearFunction',", + " subType: this.subType,", + " keys: this.keys.map(([color, pos]) => ({ value: ValueToJSON(color, this.subType), pos: pos })),", + " };", + " }", + " static fromJSON(json) {", + " return new ContinuousLinearFunction(json.keys.map((pair) => [JSONToValue(pair.value, json.subType), pair.pos]), json.subType);", + " }", + " clone() {", + " if (this.subType === 'Number') {", + " return new ContinuousLinearFunction(this.keys.map(([value, pos]) => [value, pos]), this.subType);", + " }", + " else {", + " return new ContinuousLinearFunction(this.keys.map(([value, pos]) => [value.clone(), pos]), this.subType);", + " }", + " }", + "}", + "", + "const tempVec3 = new THREE.Vector3();", + "class Gradient {", + " constructor(color = [", + " [new THREE.Vector3(0, 0, 0), 0],", + " [new THREE.Vector3(1, 1, 1), 0],", + " ], alpha = [", + " [1, 0],", + " [1, 1],", + " ]) {", + " this.type = 'function';", + " this.color = new ContinuousLinearFunction(color, 'Color');", + " this.alpha = new ContinuousLinearFunction(alpha, 'Number');", + " }", + " genColor(color, t) {", + " this.color.genValue(tempVec3, t);", + " return color.set(tempVec3.x, tempVec3.y, tempVec3.z, this.alpha.genValue(1, t));", + " }", + " toJSON() {", + " return {", + " type: 'Gradient',", + " color: this.color.toJSON(),", + " alpha: this.alpha.toJSON(),", + " };", + " }", + " static fromJSON(json) {", + " if (json.functions) {", + " const keys = json.functions.map((func) => [ColorRange.fromJSON(func.function).a, func.start]);", + " if (json.functions.length > 0) {", + " keys.push([ColorRange.fromJSON(json.functions[json.functions.length - 1].function).b, 1]);", + " }", + " return new Gradient(keys.map((key) => [new THREE.Vector3(key[0].x, key[0].y, key[0].z), key[1]]), keys.map((key) => [key[0].w, key[1]]));", + " }", + " else {", + " const gradient = new Gradient();", + " gradient.alpha = ContinuousLinearFunction.fromJSON(json.alpha);", + " gradient.color = ContinuousLinearFunction.fromJSON(json.color);", + " return gradient;", + " }", + " }", + " clone() {", + " const gradient = new Gradient();", + " gradient.alpha = this.alpha.clone();", + " gradient.color = this.color.clone();", + " return gradient;", + " }", + "}", + "", + "const tempColor = new THREE.Vector4();", + "class RandomColorBetweenGradient {", + " constructor(gradient1, gradient2) {", + " this.type = 'memorizedFunction';", + " this.gradient1 = gradient1;", + " this.gradient2 = gradient2;", + " }", + " startGen(memory) {", + " memory.rand = Math.random();", + " }", + " genColor(color, t, memory) {", + " this.gradient1.genColor(color, t);", + " this.gradient2.genColor(tempColor, t);", + " if (memory && memory.rand) {", + " color.lerp(tempColor, memory.rand);", + " }", + " else {", + " color.lerp(tempColor, Math.random());", + " }", + " return color;", + " }", + " toJSON() {", + " return {", + " type: 'RandomColorBetweenGradient',", + " gradient1: this.gradient1.toJSON(),", + " gradient2: this.gradient2.toJSON(),", + " };", + " }", + " static fromJSON(json) {", + " return new RandomColorBetweenGradient(Gradient.fromJSON(json.gradient1), Gradient.fromJSON(json.gradient2));", + " }", + " clone() {", + " return new RandomColorBetweenGradient(this.gradient1.clone(), this.gradient2.clone());", + " }", + "}", + "", + "class ConstantColor {", + " constructor(color) {", + " this.color = color;", + " this.type = 'value';", + " }", + " genColor(color) {", + " return color.copy(this.color);", + " }", + " toJSON() {", + " return {", + " type: 'ConstantColor',", + " color: ColorToJSON(this.color),", + " };", + " }", + " static fromJSON(json) {", + " return new ConstantColor(JSONToColor(json.color));", + " }", + " clone() {", + " return new ConstantColor(this.color.clone());", + " }", + "}", + "function ColorGeneratorFromJSON(json) {", + " switch (json.type) {", + " case 'ConstantColor':", + " return ConstantColor.fromJSON(json);", + " case 'ColorRange':", + " return ColorRange.fromJSON(json);", + " case 'RandomColor':", + " return RandomColor.fromJSON(json);", + " case 'Gradient':", + " return Gradient.fromJSON(json);", + " case 'RandomColorBetweenGradient':", + " return RandomColorBetweenGradient.fromJSON(json);", + " default:", + " return new ConstantColor(new THREE.Vector4(1, 1, 1, 1));", + " }", + "}", + "", + "class ConstantValue {", + " constructor(value) {", + " this.value = value;", + " this.type = 'value';", + " }", + " genValue() {", + " return this.value;", + " }", + " toJSON() {", + " return {", + " type: \"ConstantValue\",", + " value: this.value", + " };", + " }", + " static fromJSON(json) {", + " return new ConstantValue(json.value);", + " }", + " clone() {", + " return new ConstantValue(this.value);", + " }", + "}", + "", + "class IntervalValue {", + " constructor(a, b) {", + " this.a = a;", + " this.b = b;", + " this.type = 'value';", + " }", + " genValue() {", + " return THREE.MathUtils.lerp(this.a, this.b, Math.random());", + " }", + " toJSON() {", + " return {", + " type: 'IntervalValue',", + " a: this.a,", + " b: this.b,", + " };", + " }", + " static fromJSON(json) {", + " return new IntervalValue(json.a, json.b);", + " }", + " clone() {", + " return new IntervalValue(this.a, this.b);", + " }", + "}", + "", + "class PiecewiseFunction {", + " constructor() {", + " this.functions = new Array();", + " }", + " findFunction(t) {", + " let mid = 0;", + " let left = 0, right = this.functions.length - 1;", + " while (left + 1 < right) {", + " mid = Math.floor((left + right) / 2);", + " if (t < this.getStartX(mid))", + " right = mid - 1;", + " else if (t > this.getEndX(mid))", + " left = mid + 1;", + " else", + " return mid;", + " }", + " for (let i = left; i <= right; i++) {", + " if (t >= this.functions[i][1] && t <= this.getEndX(i))", + " return i;", + " }", + " return -1;", + " }", + " getStartX(index) {", + " return this.functions[index][1];", + " }", + " setStartX(index, x) {", + " if (index > 0)", + " this.functions[index][1] = x;", + " }", + " getEndX(index) {", + " if (index + 1 < this.functions.length)", + " return this.functions[index + 1][1];", + " return 1;", + " }", + " setEndX(index, x) {", + " if (index + 1 < this.functions.length)", + " this.functions[index + 1][1] = x;", + " }", + " insertFunction(t, func) {", + " const index = this.findFunction(t);", + " this.functions.splice(index + 1, 0, [func, t]);", + " }", + " removeFunction(index) {", + " return this.functions.splice(index, 1)[0][0];", + " }", + " getFunction(index) {", + " return this.functions[index][0];", + " }", + " setFunction(index, func) {", + " this.functions[index][0] = func;", + " }", + " get numOfFunctions() {", + " return this.functions.length;", + " }", + "}", + "", + "class PiecewiseBezier extends PiecewiseFunction {", + " constructor(curves = [[new Bezier(0, 1.0 / 3, 1.0 / 3 * 2, 1), 0]]) {", + " super();", + " this.type = \"function\";", + " this.functions = curves;", + " }", + " genValue(t = 0) {", + " const index = this.findFunction(t);", + " if (index === -1) {", + " return 0;", + " }", + " return this.functions[index][0].genValue((t - this.getStartX(index)) / (this.getEndX(index) - this.getStartX(index)));", + " }", + " toSVG(length, segments) {", + " if (segments < 1)", + " return \"\";", + " let result = [\"M\", 0, this.functions[0][0].p[0]].join(\" \");", + " for (let i = 1.0 / segments; i <= 1; i += 1.0 / segments) {", + " result = [result, \"L\", i * length, this.genValue(i)].join(\" \");", + " }", + " return result;", + " }", + " toJSON() {", + " return {", + " type: \"PiecewiseBezier\",", + " functions: this.functions.map(([bezier, start]) => ({ function: bezier.toJSON(), start: start })),", + " };", + " }", + " static fromJSON(json) {", + " return new PiecewiseBezier(json.functions.map((piecewiseFunction) => ([Bezier.fromJSON(piecewiseFunction.function), piecewiseFunction.start])));", + " }", + " clone() {", + " return new PiecewiseBezier(this.functions.map(([bezier, start]) => ([bezier.clone(), start])));", + " }", + "}", + "", + "function ValueGeneratorFromJSON(json) {", + " switch (json.type) {", + " case 'ConstantValue':", + " return ConstantValue.fromJSON(json);", + " case 'IntervalValue':", + " return IntervalValue.fromJSON(json);", + " case 'PiecewiseBezier':", + " return PiecewiseBezier.fromJSON(json);", + " default:", + " return new ConstantValue(0);", + " }", + "}", + "", + "class RandomQuatGenerator {", + " constructor() {", + " this.type = \"rotation\";", + " }", + " genValue(quat, t) {", + " let x, y, z, u, v, w;", + " do {", + " x = Math.random() * 2 - 1;", + " y = Math.random() * 2 - 1;", + " z = x * x + y * y;", + " } while (z > 1);", + " do {", + " u = Math.random() * 2 - 1;", + " v = Math.random() * 2 - 1;", + " w = u * u + v * v;", + " } while (w > 1);", + " const s = Math.sqrt((1 - z) / w);", + " quat.set(x, y, s * u, s * v);", + " return quat;", + " }", + " toJSON() {", + " return {", + " type: \"RandomQuat\"", + " };", + " }", + " static fromJSON(json) {", + " return new RandomQuatGenerator();", + " }", + " clone() {", + " return new RandomQuatGenerator();", + " }", + "}", + "", + "class AxisAngleGenerator {", + " constructor(axis, angle) {", + " this.axis = axis;", + " this.angle = angle;", + " this.type = 'rotation';", + " }", + " genValue(quat, t) {", + " return quat.setFromAxisAngle(this.axis, this.angle.genValue(t));", + " }", + " toJSON() {", + " return {", + " type: 'AxisAngle',", + " axis: { x: this.axis.x, y: this.axis.y, z: this.axis.z },", + " angle: this.angle.toJSON(),", + " };", + " }", + " static fromJSON(json) {", + " return new AxisAngleGenerator(new THREE.Vector3(json.axis.x, json.axis.y, json.axis.z), ValueGeneratorFromJSON(json.angle));", + " }", + " clone() {", + " return new AxisAngleGenerator(this.axis.clone(), this.angle.clone());", + " }", + "}", + "", + "class EulerGenerator {", + " constructor(angleX, angleY, angleZ, eulerOrder) {", + " this.angleX = angleX;", + " this.angleY = angleY;", + " this.angleZ = angleZ;", + " this.type = 'rotation';", + " this.eular = new THREE.Euler(0, 0, 0, eulerOrder);", + " }", + " genValue(quat, t) {", + " this.eular.set(this.angleX.genValue(t), this.angleY.genValue(t), this.angleZ.genValue(t));", + " return quat.setFromEuler(this.eular);", + " }", + " toJSON() {", + " return {", + " type: 'Euler',", + " angleX: this.angleX.toJSON(),", + " angleY: this.angleY.toJSON(),", + " angleZ: this.angleZ.toJSON(),", + " eulerOrder: this.eular.order,", + " };", + " }", + " static fromJSON(json) {", + " return new EulerGenerator(ValueGeneratorFromJSON(json.angleX), ValueGeneratorFromJSON(json.angleY), ValueGeneratorFromJSON(json.angleZ), json.eulerOrder);", + " }", + " clone() {", + " return new EulerGenerator(this.angleX, this.angleY, this.angleZ, this.eular.order);", + " }", + "}", + "", + "function RotationGeneratorFromJSON(json) {", + " switch (json.type) {", + " case 'AxisAngle':", + " return AxisAngleGenerator.fromJSON(json);", + " case 'Euler':", + " return EulerGenerator.fromJSON(json);", + " case 'RandomQuat':", + " return RandomQuatGenerator.fromJSON(json);", + " default:", + " return new RandomQuatGenerator();", + " }", + "}", + "", + "function GeneratorFromJSON(json) {", + " switch (json.type) {", + " case 'ConstantValue':", + " case 'IntervalValue':", + " case 'PiecewiseBezier':", + " return ValueGeneratorFromJSON(json);", + " case 'AxisAngle':", + " case 'RandomQuat':", + " case 'Euler':", + " return RotationGeneratorFromJSON(json);", + " default:", + " return new ConstantValue(0);", + " }", + "}", + "", + "class ColorOverLife {", + " constructor(color) {", + " this.color = color;", + " this.type = 'ColorOverLife';", + " }", + " initialize(particle) {", + " if (this.color.type === 'memorizedFunction') {", + " particle.colorOverLifeMemory = {};", + " this.color.startGen(particle.colorOverLifeMemory);", + " }", + " }", + " update(particle, delta) {", + " if (this.color.type === 'memorizedFunction') {", + " this.color.genColor(particle.color, particle.age / particle.life, particle.colorOverLifeMemory);", + " }", + " else {", + " this.color.genColor(particle.color, particle.age / particle.life);", + " }", + " particle.color.x *= particle.startColor.x;", + " particle.color.y *= particle.startColor.y;", + " particle.color.z *= particle.startColor.z;", + " particle.color.w *= particle.startColor.w;", + " }", + " frameUpdate(delta) { }", + " toJSON() {", + " return {", + " type: this.type,", + " color: this.color.toJSON(),", + " };", + " }", + " static fromJSON(json) {", + " return new ColorOverLife(ColorGeneratorFromJSON(json.color));", + " }", + " clone() {", + " return new ColorOverLife(this.color.clone());", + " }", + " reset() { }", + "}", + "", + "class RotationOverLife {", + " constructor(angularVelocity) {", + " this.angularVelocity = angularVelocity;", + " this.type = 'RotationOverLife';", + " this.dynamic = !(angularVelocity instanceof ConstantValue || angularVelocity instanceof IntervalValue);", + " }", + " initialize(particle) {", + " this.dynamic = !(this.angularVelocity instanceof ConstantValue || this.angularVelocity instanceof IntervalValue);", + " if (!this.dynamic && typeof particle.rotation === 'number') {", + " particle.angularVelocity = this.angularVelocity.genValue();", + " }", + " }", + " update(particle, delta) {", + " if (!this.dynamic) {", + " if (typeof particle.rotation === 'number') {", + " particle.rotation += delta * particle.angularVelocity;", + " }", + " }", + " else {", + " if (typeof particle.rotation === 'number') {", + " particle.rotation +=", + " delta * this.angularVelocity.genValue(particle.age / particle.life);", + " }", + " }", + " }", + " toJSON() {", + " return {", + " type: this.type,", + " angularVelocity: this.angularVelocity.toJSON(),", + " };", + " }", + " static fromJSON(json) {", + " return new RotationOverLife(ValueGeneratorFromJSON(json.angularVelocity));", + " }", + " frameUpdate(delta) { }", + " clone() {", + " return new RotationOverLife(this.angularVelocity.clone());", + " }", + " reset() { }", + "}", + "", + "const IdentityQuaternion = new THREE.Quaternion();", + "class Rotation3DOverLife {", + " constructor(angularVelocity) {", + " this.angularVelocity = angularVelocity;", + " this.type = 'Rotation3DOverLife';", + " this.tempQuat = new THREE.Quaternion();", + " this.dynamic = !(angularVelocity instanceof RandomQuatGenerator);", + " }", + " initialize(particle) {", + " this.dynamic = !(this.angularVelocity instanceof RandomQuatGenerator);", + " if (particle.rotation instanceof THREE.Quaternion) {", + " particle.angularVelocity = new THREE.Quaternion();", + " this.angularVelocity.genValue(particle.angularVelocity);", + " }", + " }", + " update(particle, delta) {", + " if (particle.rotation instanceof THREE.Quaternion) {", + " if (!this.dynamic) {", + " this.tempQuat.slerpQuaternions(IdentityQuaternion, particle.angularVelocity, delta);", + " particle.rotation.multiply(this.tempQuat);", + " }", + " else {", + " this.angularVelocity.genValue(this.tempQuat, particle.age / particle.life);", + " this.tempQuat.slerpQuaternions(IdentityQuaternion, this.tempQuat, delta);", + " particle.rotation.multiply(this.tempQuat);", + " }", + " }", + " }", + " toJSON() {", + " return {", + " type: this.type,", + " angularVelocity: this.angularVelocity.toJSON(),", + " };", + " }", + " static fromJSON(json) {", + " return new Rotation3DOverLife(RotationGeneratorFromJSON(json.angularVelocity));", + " }", + " frameUpdate(delta) { }", + " clone() {", + " return new Rotation3DOverLife(this.angularVelocity.clone());", + " }", + " reset() { }", + "}", + "", + "class ForceOverLife {", + " initialize(particle) { }", + " constructor(x, y, z) {", + " this.x = x;", + " this.y = y;", + " this.z = z;", + " this.type = 'ForceOverLife';", + " this._temp = new THREE.Vector3();", + " }", + " update(particle, delta) {", + " this._temp.set(this.x.genValue(particle.age / particle.life), this.y.genValue(particle.age / particle.life), this.z.genValue(particle.age / particle.life));", + " particle.velocity.addScaledVector(this._temp, delta);", + " }", + " toJSON() {", + " return {", + " type: this.type,", + " x: this.x.toJSON(),", + " y: this.y.toJSON(),", + " z: this.z.toJSON(),", + " };", + " }", + " static fromJSON(json) {", + " return new ForceOverLife(ValueGeneratorFromJSON(json.x), ValueGeneratorFromJSON(json.y), ValueGeneratorFromJSON(json.z));", + " }", + " frameUpdate(delta) { }", + " clone() {", + " return new ForceOverLife(this.x.clone(), this.y.clone(), this.z.clone());", + " }", + " reset() { }", + "}", + "", + "class SizeOverLife {", + " initialize(particle) {", + " }", + " constructor(size) {", + " this.size = size;", + " this.type = 'SizeOverLife';", + " }", + " update(particle) {", + " particle.size = particle.startSize * this.size.genValue(particle.age / particle.life);", + " }", + " toJSON() {", + " return {", + " type: this.type,", + " size: this.size.toJSON(),", + " };", + " }", + " static fromJSON(json) {", + " return new SizeOverLife(ValueGeneratorFromJSON(json.size));", + " }", + " frameUpdate(delta) {", + " }", + " clone() {", + " return new SizeOverLife(this.size.clone());", + " }", + " reset() {", + " }", + "}", + "", + "class SpeedOverLife {", + " initialize(particle) {", + " }", + " constructor(speed) {", + " this.speed = speed;", + " this.type = 'SpeedOverLife';", + " }", + " update(particle) {", + " particle.speedModifier = this.speed.genValue(particle.age / particle.life);", + " }", + " toJSON() {", + " return {", + " type: this.type,", + " speed: this.speed.toJSON(),", + " };", + " }", + " static fromJSON(json) {", + " return new SpeedOverLife(ValueGeneratorFromJSON(json.speed));", + " }", + " frameUpdate(delta) {", + " }", + " clone() {", + " return new SpeedOverLife(this.speed.clone());", + " }", + " reset() {", + " }", + "}", + "", + "class FrameOverLife {", + " constructor(frame) {", + " this.frame = frame;", + " this.type = 'FrameOverLife';", + " }", + " initialize(particle) {", + " if (!(this.frame instanceof PiecewiseBezier)) {", + " particle.uvTile = Math.floor(this.frame.genValue(0) + 0.001);", + " }", + " }", + " update(particle, delta) {", + " if (this.frame instanceof PiecewiseBezier) {", + " particle.uvTile = Math.floor(this.frame.genValue(particle.age / particle.life) + 0.001);", + " }", + " }", + " frameUpdate(delta) { }", + " toJSON() {", + " return {", + " type: this.type,", + " frame: this.frame.toJSON(),", + " };", + " }", + " static fromJSON(json) {", + " return new FrameOverLife(ValueGeneratorFromJSON(json.frame));", + " }", + " clone() {", + " return new FrameOverLife(this.frame.clone());", + " }", + " reset() { }", + "}", + "", + "new THREE.Vector3(0, 0, 1);", + "class OrbitOverLife {", + " constructor(orbitSpeed, axis = new THREE.Vector3(0, 1, 0)) {", + " this.orbitSpeed = orbitSpeed;", + " this.axis = axis;", + " this.type = 'OrbitOverLife';", + " this.temp = new THREE.Vector3();", + " this.rotation = new THREE.Quaternion();", + " this.line = new THREE.Line3();", + " }", + " initialize(particle) {", + " }", + " update(particle, delta) {", + " this.line.set(new THREE.Vector3(0, 0, 0), this.axis);", + " this.line.closestPointToPoint(particle.position, false, this.temp);", + " this.rotation.setFromAxisAngle(this.axis, this.orbitSpeed.genValue(particle.age / particle.life) * delta);", + " particle.position.sub(this.temp);", + " particle.position.applyQuaternion(this.rotation);", + " particle.position.add(this.temp);", + " }", + " frameUpdate(delta) {", + " }", + " toJSON() {", + " return {", + " type: this.type,", + " orbitSpeed: this.orbitSpeed.toJSON(),", + " axis: [this.axis.x, this.axis.y, this.axis.z],", + " };", + " }", + " static fromJSON(json) {", + " return new OrbitOverLife(ValueGeneratorFromJSON(json.orbitSpeed), json.axis ? new THREE.Vector3(json.axis[0], json.axis[1], json.axis[2]) : undefined);", + " }", + " clone() {", + " return new OrbitOverLife(this.orbitSpeed.clone());", + " }", + " reset() {", + " }", + "}", + "", + "class WidthOverLength {", + " initialize(particle) {", + " }", + " constructor(width) {", + " this.width = width;", + " this.type = 'WidthOverLength';", + " }", + " update(particle) {", + " if (particle instanceof TrailParticle) {", + " const iter = particle.previous.values();", + " for (let i = 0; i < particle.previous.length; i++) {", + " const cur = iter.next();", + " // We added a multiplication with size because it's more convenient for the extension.", + " cur.value.size = this.width.genValue((particle.previous.length - i) / particle.length) * particle.size;", + " }", + " }", + " }", + " frameUpdate(delta) {", + " }", + " toJSON() {", + " return {", + " type: this.type,", + " width: this.width.toJSON(),", + " };", + " }", + " static fromJSON(json) {", + " return new WidthOverLength(ValueGeneratorFromJSON(json.width));", + " }", + " clone() {", + " return new WidthOverLength(this.width.clone());", + " }", + " reset() {", + " }", + "}", + "", + "class ApplyForce {", + " constructor(direction, magnitude) {", + " this.direction = direction;", + " this.magnitude = magnitude;", + " this.type = 'ApplyForce';", + " this.magnitudeValue = this.magnitude.genValue();", + " }", + " initialize(particle) {", + " }", + " update(particle, delta) {", + " particle.velocity.addScaledVector(this.direction, this.magnitudeValue * delta);", + " }", + " frameUpdate(delta) {", + " this.magnitudeValue = this.magnitude.genValue();", + " }", + " toJSON() {", + " return {", + " type: this.type,", + " direction: [this.direction.x, this.direction.y, this.direction.z],", + " magnitude: this.magnitude.toJSON(),", + " };", + " }", + " static fromJSON(json) {", + " var _a;", + " return new ApplyForce(new THREE.Vector3(json.direction[0], json.direction[1], json.direction[2]), ValueGeneratorFromJSON((_a = json.magnitude) !== null && _a !== void 0 ? _a : json.force));", + " }", + " clone() {", + " return new ApplyForce(this.direction.clone(), this.magnitude.clone());", + " }", + " reset() {", + " }", + "}", + "", + "class GravityForce {", + " constructor(center, magnitude) {", + " this.center = center;", + " this.magnitude = magnitude;", + " this.type = 'GravityForce';", + " this.temp = new THREE.Vector3();", + " }", + " initialize(particle) {", + " }", + " update(particle, delta) {", + " this.temp.copy(this.center).sub(particle.position).normalize();", + " particle.velocity.addScaledVector(this.temp, this.magnitude / particle.position.distanceToSquared(this.center) * delta);", + " }", + " frameUpdate(delta) {", + " }", + " toJSON() {", + " return {", + " type: this.type,", + " center: [this.center.x, this.center.y, this.center.z],", + " magnitude: this.magnitude,", + " };", + " }", + " static fromJSON(json) {", + " return new GravityForce(new THREE.Vector3(json.center[0], json.center[1], json.center[2]), json.magnitude);", + " }", + " clone() {", + " return new GravityForce(this.center.clone(), this.magnitude);", + " }", + " reset() {", + " }", + "}", + "", + "new THREE.Vector3(0, 0, 1);", + "class ChangeEmitDirection {", + " constructor(angle) {", + " this.angle = angle;", + " this.type = 'ChangeEmitDirection';", + " this._temp = new THREE.Vector3();", + " this._q = new THREE.Quaternion();", + " }", + " initialize(particle) {", + " const len = particle.velocity.length();", + " if (len == 0)", + " return;", + " particle.velocity.normalize();", + " if (particle.velocity.x === 0 && particle.velocity.y === 0) {", + " this._temp.set(0, particle.velocity.z, 0);", + " }", + " else {", + " this._temp.set(-particle.velocity.y, particle.velocity.x, 0);", + " }", + " this._q.setFromAxisAngle(this._temp.normalize(), this.angle.genValue());", + " this._temp.copy(particle.velocity);", + " particle.velocity.applyQuaternion(this._q);", + " this._q.setFromAxisAngle(this._temp, Math.random() * Math.PI * 2);", + " particle.velocity.applyQuaternion(this._q);", + " particle.velocity.setLength(len);", + " }", + " update(particle, delta) {", + " }", + " frameUpdate(delta) {", + " }", + " toJSON() {", + " return {", + " type: this.type,", + " angle: this.angle.toJSON(),", + " };", + " }", + " static fromJSON(json) {", + " return new ChangeEmitDirection(ValueGeneratorFromJSON(json.angle));", + " }", + " clone() {", + " return new ChangeEmitDirection(this.angle);", + " }", + " reset() {", + " }", + "}", + "", + "const VECTOR_ONE = new THREE.Vector3(1, 1, 1);", + "const VECTOR_Z = new THREE.Vector3(0, 0, 1);", + "let SubParticleEmitMode = void 0;", + "(function (SubParticleEmitMode) {", + " SubParticleEmitMode[SubParticleEmitMode[\"Death\"] = 0] = \"Death\";", + " SubParticleEmitMode[SubParticleEmitMode[\"Birth\"] = 1] = \"Birth\";", + " SubParticleEmitMode[SubParticleEmitMode[\"Frame\"] = 2] = \"Frame\";", + "})(SubParticleEmitMode || (SubParticleEmitMode = {}));", + "class EmitSubParticleSystem {", + " constructor(particleSystem, useVelocityAsBasis, subParticleSystem, mode = SubParticleEmitMode.Frame, emitProbability = 1) {", + " this.particleSystem = particleSystem;", + " this.useVelocityAsBasis = useVelocityAsBasis;", + " this.subParticleSystem = subParticleSystem;", + " this.mode = mode;", + " this.emitProbability = emitProbability;", + " this.type = 'EmitSubParticleSystem';", + " this.q_ = new THREE.Quaternion();", + " this.v_ = new THREE.Vector3();", + " this.v2_ = new THREE.Vector3();", + " this.subEmissions = new Array();", + " if (this.subParticleSystem && this.subParticleSystem.system) {", + " this.subParticleSystem.system.onlyUsedByOther = true;", + " }", + " }", + " initialize(particle) {", + " }", + " update(particle, delta) {", + " if (this.mode === SubParticleEmitMode.Frame) {", + " this.emit(particle, delta);", + " }", + " else if (this.mode === SubParticleEmitMode.Birth && particle.age === 0) {", + " this.emit(particle, delta);", + " }", + " else if (this.mode === SubParticleEmitMode.Death && particle.age + delta >= particle.life) {", + " this.emit(particle, delta);", + " }", + " }", + " emit(particle, delta) {", + " if (!this.subParticleSystem)", + " return;", + " if (Math.random() > this.emitProbability) {", + " return;", + " }", + " const m = new THREE.Matrix4();", + " this.setMatrixFromParticle(m, particle);", + " this.subEmissions.push({", + " burstIndex: 0,", + " burstWaveIndex: 0,", + " time: 0,", + " waitEmiting: 0,", + " matrix: m,", + " travelDistance: 0,", + " particle: particle,", + " });", + " }", + " frameUpdate(delta) {", + " if (!this.subParticleSystem)", + " return;", + " for (let i = 0; i < this.subEmissions.length; i++) {", + " if (this.subEmissions[i].time >= this.subParticleSystem.system.duration) {", + " this.subEmissions[i] = this.subEmissions[this.subEmissions.length - 1];", + " this.subEmissions.length = this.subEmissions.length - 1;", + " i--;", + " }", + " else {", + " let subEmissionState = this.subEmissions[i];", + " if (subEmissionState.particle && subEmissionState.particle.age < subEmissionState.particle.life) {", + " this.setMatrixFromParticle(subEmissionState.matrix, subEmissionState.particle);", + " }", + " else {", + " subEmissionState.particle = undefined;", + " }", + " this.subParticleSystem.system.emit(delta, subEmissionState, subEmissionState.matrix);", + " }", + " }", + " }", + " toJSON() {", + " return {", + " type: this.type,", + " subParticleSystem: this.subParticleSystem ? this.subParticleSystem.uuid : '',", + " useVelocityAsBasis: this.useVelocityAsBasis,", + " mode: this.mode,", + " emitProbability: this.emitProbability,", + " };", + " }", + " static fromJSON(json, particleSystem) {", + " return new EmitSubParticleSystem(particleSystem, json.useVelocityAsBasis, json.subParticleSystem, json.mode, json.emitProbability);", + " }", + " clone() {", + " return new EmitSubParticleSystem(this.particleSystem, this.useVelocityAsBasis, this.subParticleSystem, this.mode, this.emitProbability);", + " }", + " reset() { }", + " setMatrixFromParticle(m, particle) {", + " let rotation;", + " if (particle.rotation === undefined || this.useVelocityAsBasis) {", + " if (particle.velocity.x === 0 &&", + " particle.velocity.y === 0 &&", + " (particle.velocity.z === 1 || particle.velocity.z === 0)) {", + " m.set(1, 0, 0, particle.position.x, 0, 1, 0, particle.position.y, 0, 0, 1, particle.position.z, 0, 0, 0, 1);", + " }", + " else {", + " this.v_.copy(VECTOR_Z).cross(particle.velocity);", + " this.v2_.copy(particle.velocity).cross(this.v_);", + " const len = this.v_.length();", + " const len2 = this.v2_.length();", + " m.set(this.v_.x / len, this.v2_.x / len2, particle.velocity.x, particle.position.x, this.v_.y / len, this.v2_.y / len2, particle.velocity.y, particle.position.y, this.v_.z / len, this.v2_.z / len2, particle.velocity.z, particle.position.z, 0, 0, 0, 1);", + " }", + " }", + " else {", + " if (particle.rotation instanceof THREE.Quaternion) {", + " rotation = particle.rotation;", + " }", + " else {", + " this.q_.setFromAxisAngle(VECTOR_Z, particle.rotation);", + " rotation = this.q_;", + " }", + " m.compose(particle.position, rotation, VECTOR_ONE);", + " }", + " if (!this.particleSystem.worldSpace) {", + " m.multiplyMatrices(this.particleSystem.emitter.matrixWorld, m);", + " }", + " }", + "}", + "", + "const F2 = 0.5 * (Math.sqrt(3.0) - 1.0);", + "const G2 = (3.0 - Math.sqrt(3.0)) / 6.0;", + "const F3 = 1.0 / 3.0;", + "const G3 = 1.0 / 6.0;", + "const F4 = (Math.sqrt(5.0) - 1.0) / 4.0;", + "const G4 = (5.0 - Math.sqrt(5.0)) / 20.0;", + "const grad3 = new Float32Array([1, 1, 0,", + " -1, 1, 0,", + " 1, -1, 0,", + " -1, -1, 0,", + " 1, 0, 1,", + " -1, 0, 1,", + " 1, 0, -1,", + " -1, 0, -1,", + " 0, 1, 1,", + " 0, -1, 1,", + " 0, 1, -1,", + " 0, -1, -1]);", + "const grad4 = new Float32Array([0, 1, 1, 1, 0, 1, 1, -1, 0, 1, -1, 1, 0, 1, -1, -1,", + " 0, -1, 1, 1, 0, -1, 1, -1, 0, -1, -1, 1, 0, -1, -1, -1,", + " 1, 0, 1, 1, 1, 0, 1, -1, 1, 0, -1, 1, 1, 0, -1, -1,", + " -1, 0, 1, 1, -1, 0, 1, -1, -1, 0, -1, 1, -1, 0, -1, -1,", + " 1, 1, 0, 1, 1, 1, 0, -1, 1, -1, 0, 1, 1, -1, 0, -1,", + " -1, 1, 0, 1, -1, 1, 0, -1, -1, -1, 0, 1, -1, -1, 0, -1,", + " 1, 1, 1, 0, 1, 1, -1, 0, 1, -1, 1, 0, 1, -1, -1, 0,", + " -1, 1, 1, 0, -1, 1, -1, 0, -1, -1, 1, 0, -1, -1, -1, 0]);", + "class SimplexNoise {", + " constructor(randomOrSeed = Math.random) {", + " const random = typeof randomOrSeed == 'function' ? randomOrSeed : alea(randomOrSeed);", + " this.p = buildPermutationTable(random);", + " this.perm = new Uint8Array(512);", + " this.permMod12 = new Uint8Array(512);", + " for (let i = 0; i < 512; i++) {", + " this.perm[i] = this.p[i & 255];", + " this.permMod12[i] = this.perm[i] % 12;", + " }", + " }", + " noise2D(x, y) {", + " const permMod12 = this.permMod12;", + " const perm = this.perm;", + " let n0 = 0;", + " let n1 = 0;", + " let n2 = 0;", + " const s = (x + y) * F2;", + " const i = Math.floor(x + s);", + " const j = Math.floor(y + s);", + " const t = (i + j) * G2;", + " const X0 = i - t;", + " const Y0 = j - t;", + " const x0 = x - X0;", + " const y0 = y - Y0;", + " let i1, j1;", + " if (x0 > y0) {", + " i1 = 1;", + " j1 = 0;", + " }", + " else {", + " i1 = 0;", + " j1 = 1;", + " }", + " const x1 = x0 - i1 + G2;", + " const y1 = y0 - j1 + G2;", + " const x2 = x0 - 1.0 + 2.0 * G2;", + " const y2 = y0 - 1.0 + 2.0 * G2;", + " const ii = i & 255;", + " const jj = j & 255;", + " let t0 = 0.5 - x0 * x0 - y0 * y0;", + " if (t0 >= 0) {", + " const gi0 = permMod12[ii + perm[jj]] * 3;", + " t0 *= t0;", + " n0 = t0 * t0 * (grad3[gi0] * x0 + grad3[gi0 + 1] * y0);", + " }", + " let t1 = 0.5 - x1 * x1 - y1 * y1;", + " if (t1 >= 0) {", + " const gi1 = permMod12[ii + i1 + perm[jj + j1]] * 3;", + " t1 *= t1;", + " n1 = t1 * t1 * (grad3[gi1] * x1 + grad3[gi1 + 1] * y1);", + " }", + " let t2 = 0.5 - x2 * x2 - y2 * y2;", + " if (t2 >= 0) {", + " const gi2 = permMod12[ii + 1 + perm[jj + 1]] * 3;", + " t2 *= t2;", + " n2 = t2 * t2 * (grad3[gi2] * x2 + grad3[gi2 + 1] * y2);", + " }", + " return 70.0 * (n0 + n1 + n2);", + " }", + " noise3D(x, y, z) {", + " const permMod12 = this.permMod12;", + " const perm = this.perm;", + " let n0, n1, n2, n3;", + " const s = (x + y + z) * F3;", + " const i = Math.floor(x + s);", + " const j = Math.floor(y + s);", + " const k = Math.floor(z + s);", + " const t = (i + j + k) * G3;", + " const X0 = i - t;", + " const Y0 = j - t;", + " const Z0 = k - t;", + " const x0 = x - X0;", + " const y0 = y - Y0;", + " const z0 = z - Z0;", + " let i1, j1, k1;", + " let i2, j2, k2;", + " if (x0 >= y0) {", + " if (y0 >= z0) {", + " i1 = 1;", + " j1 = 0;", + " k1 = 0;", + " i2 = 1;", + " j2 = 1;", + " k2 = 0;", + " }", + " else if (x0 >= z0) {", + " i1 = 1;", + " j1 = 0;", + " k1 = 0;", + " i2 = 1;", + " j2 = 0;", + " k2 = 1;", + " }", + " else {", + " i1 = 0;", + " j1 = 0;", + " k1 = 1;", + " i2 = 1;", + " j2 = 0;", + " k2 = 1;", + " }", + " }", + " else {", + " if (y0 < z0) {", + " i1 = 0;", + " j1 = 0;", + " k1 = 1;", + " i2 = 0;", + " j2 = 1;", + " k2 = 1;", + " }", + " else if (x0 < z0) {", + " i1 = 0;", + " j1 = 1;", + " k1 = 0;", + " i2 = 0;", + " j2 = 1;", + " k2 = 1;", + " }", + " else {", + " i1 = 0;", + " j1 = 1;", + " k1 = 0;", + " i2 = 1;", + " j2 = 1;", + " k2 = 0;", + " }", + " }", + " const x1 = x0 - i1 + G3;", + " const y1 = y0 - j1 + G3;", + " const z1 = z0 - k1 + G3;", + " const x2 = x0 - i2 + 2.0 * G3;", + " const y2 = y0 - j2 + 2.0 * G3;", + " const z2 = z0 - k2 + 2.0 * G3;", + " const x3 = x0 - 1.0 + 3.0 * G3;", + " const y3 = y0 - 1.0 + 3.0 * G3;", + " const z3 = z0 - 1.0 + 3.0 * G3;", + " const ii = i & 255;", + " const jj = j & 255;", + " const kk = k & 255;", + " let t0 = 0.6 - x0 * x0 - y0 * y0 - z0 * z0;", + " if (t0 < 0)", + " n0 = 0.0;", + " else {", + " const gi0 = permMod12[ii + perm[jj + perm[kk]]] * 3;", + " t0 *= t0;", + " n0 = t0 * t0 * (grad3[gi0] * x0 + grad3[gi0 + 1] * y0 + grad3[gi0 + 2] * z0);", + " }", + " let t1 = 0.6 - x1 * x1 - y1 * y1 - z1 * z1;", + " if (t1 < 0)", + " n1 = 0.0;", + " else {", + " const gi1 = permMod12[ii + i1 + perm[jj + j1 + perm[kk + k1]]] * 3;", + " t1 *= t1;", + " n1 = t1 * t1 * (grad3[gi1] * x1 + grad3[gi1 + 1] * y1 + grad3[gi1 + 2] * z1);", + " }", + " let t2 = 0.6 - x2 * x2 - y2 * y2 - z2 * z2;", + " if (t2 < 0)", + " n2 = 0.0;", + " else {", + " const gi2 = permMod12[ii + i2 + perm[jj + j2 + perm[kk + k2]]] * 3;", + " t2 *= t2;", + " n2 = t2 * t2 * (grad3[gi2] * x2 + grad3[gi2 + 1] * y2 + grad3[gi2 + 2] * z2);", + " }", + " let t3 = 0.6 - x3 * x3 - y3 * y3 - z3 * z3;", + " if (t3 < 0)", + " n3 = 0.0;", + " else {", + " const gi3 = permMod12[ii + 1 + perm[jj + 1 + perm[kk + 1]]] * 3;", + " t3 *= t3;", + " n3 = t3 * t3 * (grad3[gi3] * x3 + grad3[gi3 + 1] * y3 + grad3[gi3 + 2] * z3);", + " }", + " return 32.0 * (n0 + n1 + n2 + n3);", + " }", + " noise4D(x, y, z, w) {", + " const perm = this.perm;", + " let n0, n1, n2, n3, n4;", + " const s = (x + y + z + w) * F4;", + " const i = Math.floor(x + s);", + " const j = Math.floor(y + s);", + " const k = Math.floor(z + s);", + " const l = Math.floor(w + s);", + " const t = (i + j + k + l) * G4;", + " const X0 = i - t;", + " const Y0 = j - t;", + " const Z0 = k - t;", + " const W0 = l - t;", + " const x0 = x - X0;", + " const y0 = y - Y0;", + " const z0 = z - Z0;", + " const w0 = w - W0;", + " let rankx = 0;", + " let ranky = 0;", + " let rankz = 0;", + " let rankw = 0;", + " if (x0 > y0)", + " rankx++;", + " else", + " ranky++;", + " if (x0 > z0)", + " rankx++;", + " else", + " rankz++;", + " if (x0 > w0)", + " rankx++;", + " else", + " rankw++;", + " if (y0 > z0)", + " ranky++;", + " else", + " rankz++;", + " if (y0 > w0)", + " ranky++;", + " else", + " rankw++;", + " if (z0 > w0)", + " rankz++;", + " else", + " rankw++;", + " const i1 = rankx >= 3 ? 1 : 0;", + " const j1 = ranky >= 3 ? 1 : 0;", + " const k1 = rankz >= 3 ? 1 : 0;", + " const l1 = rankw >= 3 ? 1 : 0;", + " const i2 = rankx >= 2 ? 1 : 0;", + " const j2 = ranky >= 2 ? 1 : 0;", + " const k2 = rankz >= 2 ? 1 : 0;", + " const l2 = rankw >= 2 ? 1 : 0;", + " const i3 = rankx >= 1 ? 1 : 0;", + " const j3 = ranky >= 1 ? 1 : 0;", + " const k3 = rankz >= 1 ? 1 : 0;", + " const l3 = rankw >= 1 ? 1 : 0;", + " const x1 = x0 - i1 + G4;", + " const y1 = y0 - j1 + G4;", + " const z1 = z0 - k1 + G4;", + " const w1 = w0 - l1 + G4;", + " const x2 = x0 - i2 + 2.0 * G4;", + " const y2 = y0 - j2 + 2.0 * G4;", + " const z2 = z0 - k2 + 2.0 * G4;", + " const w2 = w0 - l2 + 2.0 * G4;", + " const x3 = x0 - i3 + 3.0 * G4;", + " const y3 = y0 - j3 + 3.0 * G4;", + " const z3 = z0 - k3 + 3.0 * G4;", + " const w3 = w0 - l3 + 3.0 * G4;", + " const x4 = x0 - 1.0 + 4.0 * G4;", + " const y4 = y0 - 1.0 + 4.0 * G4;", + " const z4 = z0 - 1.0 + 4.0 * G4;", + " const w4 = w0 - 1.0 + 4.0 * G4;", + " const ii = i & 255;", + " const jj = j & 255;", + " const kk = k & 255;", + " const ll = l & 255;", + " let t0 = 0.6 - x0 * x0 - y0 * y0 - z0 * z0 - w0 * w0;", + " if (t0 < 0)", + " n0 = 0.0;", + " else {", + " const gi0 = (perm[ii + perm[jj + perm[kk + perm[ll]]]] % 32) * 4;", + " t0 *= t0;", + " n0 = t0 * t0 * (grad4[gi0] * x0 + grad4[gi0 + 1] * y0 + grad4[gi0 + 2] * z0 + grad4[gi0 + 3] * w0);", + " }", + " let t1 = 0.6 - x1 * x1 - y1 * y1 - z1 * z1 - w1 * w1;", + " if (t1 < 0)", + " n1 = 0.0;", + " else {", + " const gi1 = (perm[ii + i1 + perm[jj + j1 + perm[kk + k1 + perm[ll + l1]]]] % 32) * 4;", + " t1 *= t1;", + " n1 = t1 * t1 * (grad4[gi1] * x1 + grad4[gi1 + 1] * y1 + grad4[gi1 + 2] * z1 + grad4[gi1 + 3] * w1);", + " }", + " let t2 = 0.6 - x2 * x2 - y2 * y2 - z2 * z2 - w2 * w2;", + " if (t2 < 0)", + " n2 = 0.0;", + " else {", + " const gi2 = (perm[ii + i2 + perm[jj + j2 + perm[kk + k2 + perm[ll + l2]]]] % 32) * 4;", + " t2 *= t2;", + " n2 = t2 * t2 * (grad4[gi2] * x2 + grad4[gi2 + 1] * y2 + grad4[gi2 + 2] * z2 + grad4[gi2 + 3] * w2);", + " }", + " let t3 = 0.6 - x3 * x3 - y3 * y3 - z3 * z3 - w3 * w3;", + " if (t3 < 0)", + " n3 = 0.0;", + " else {", + " const gi3 = (perm[ii + i3 + perm[jj + j3 + perm[kk + k3 + perm[ll + l3]]]] % 32) * 4;", + " t3 *= t3;", + " n3 = t3 * t3 * (grad4[gi3] * x3 + grad4[gi3 + 1] * y3 + grad4[gi3 + 2] * z3 + grad4[gi3 + 3] * w3);", + " }", + " let t4 = 0.6 - x4 * x4 - y4 * y4 - z4 * z4 - w4 * w4;", + " if (t4 < 0)", + " n4 = 0.0;", + " else {", + " const gi4 = (perm[ii + 1 + perm[jj + 1 + perm[kk + 1 + perm[ll + 1]]]] % 32) * 4;", + " t4 *= t4;", + " n4 = t4 * t4 * (grad4[gi4] * x4 + grad4[gi4 + 1] * y4 + grad4[gi4 + 2] * z4 + grad4[gi4 + 3] * w4);", + " }", + " return 27.0 * (n0 + n1 + n2 + n3 + n4);", + " }", + "}", + "function buildPermutationTable(random) {", + " const p = new Uint8Array(256);", + " for (let i = 0; i < 256; i++) {", + " p[i] = i;", + " }", + " for (let i = 0; i < 255; i++) {", + " const r = i + ~~(random() * (256 - i));", + " const aux = p[i];", + " p[i] = p[r];", + " p[r] = aux;", + " }", + " return p;", + "}", + "function alea(seed) {", + " let s0 = 0;", + " let s1 = 0;", + " let s2 = 0;", + " let c = 1;", + " const mash = masher();", + " s0 = mash(' ');", + " s1 = mash(' ');", + " s2 = mash(' ');", + " s0 -= mash(seed);", + " if (s0 < 0) {", + " s0 += 1;", + " }", + " s1 -= mash(seed);", + " if (s1 < 0) {", + " s1 += 1;", + " }", + " s2 -= mash(seed);", + " if (s2 < 0) {", + " s2 += 1;", + " }", + " return function () {", + " const t = 2091639 * s0 + c * 2.3283064365386963e-10;", + " s0 = s1;", + " s1 = s2;", + " return s2 = t - (c = t | 0);", + " };", + "}", + "function masher() {", + " let n = 0xefc8249d;", + " return function (data) {", + " data = data.toString();", + " for (let i = 0; i < data.length; i++) {", + " n += data.charCodeAt(i);", + " let h = 0.02519603282416938 * n;", + " n = h >>> 0;", + " h -= n;", + " h *= n;", + " n = h >>> 0;", + " h -= n;", + " n += h * 0x100000000;", + " }", + " return (n >>> 0) * 2.3283064365386963e-10;", + " };", + "}", + "", + "class TurbulenceField {", + " constructor(scale, octaves, velocityMultiplier, timeScale) {", + " this.scale = scale;", + " this.octaves = octaves;", + " this.velocityMultiplier = velocityMultiplier;", + " this.timeScale = timeScale;", + " this.type = 'TurbulenceField';", + " this.generator = new SimplexNoise();", + " this.timeOffset = new THREE.Vector3();", + " this.temp = new THREE.Vector3();", + " this.temp2 = new THREE.Vector3();", + " this.timeOffset.x = Math.random() / this.scale.x * this.timeScale.x;", + " this.timeOffset.y = Math.random() / this.scale.y * this.timeScale.y;", + " this.timeOffset.z = Math.random() / this.scale.z * this.timeScale.z;", + " }", + " initialize(particle) {", + " }", + " update(particle, delta) {", + " const x = particle.position.x / this.scale.x;", + " const y = particle.position.y / this.scale.y;", + " const z = particle.position.z / this.scale.z;", + " this.temp.set(0, 0, 0);", + " let lvl = 1;", + " for (let i = 0; i < this.octaves; i++) {", + " this.temp2.set(this.generator.noise4D(x * lvl, y * lvl, z * lvl, this.timeOffset.x * lvl) / lvl, this.generator.noise4D(x * lvl, y * lvl, z * lvl, this.timeOffset.y * lvl) / lvl, this.generator.noise4D(x * lvl, y * lvl, z * lvl, this.timeOffset.z * lvl) / lvl);", + " this.temp.add(this.temp2);", + " lvl *= 2;", + " }", + " this.temp.multiply(this.velocityMultiplier);", + " particle.velocity.addScaledVector(this.temp, delta);", + " }", + " toJSON() {", + " return {", + " type: this.type,", + " scale: [this.scale.x, this.scale.y, this.scale.z],", + " octaves: this.octaves,", + " velocityMultiplier: [this.velocityMultiplier.x, this.velocityMultiplier.y, this.velocityMultiplier.z],", + " timeScale: [this.timeScale.x, this.timeScale.y, this.timeScale.z],", + " };", + " }", + " frameUpdate(delta) {", + " this.timeOffset.x += delta * this.timeScale.x;", + " this.timeOffset.y += delta * this.timeScale.y;", + " this.timeOffset.z += delta * this.timeScale.z;", + " }", + " static fromJSON(json) {", + " return new TurbulenceField(new THREE.Vector3(json.scale[0], json.scale[1], json.scale[2]), json.octaves, new THREE.Vector3(json.velocityMultiplier[0], json.velocityMultiplier[1], json.velocityMultiplier[2]), new THREE.Vector3(json.timeScale[0], json.timeScale[1], json.timeScale[2]));", + " }", + " clone() {", + " return new TurbulenceField(this.scale.clone(), this.octaves, this.velocityMultiplier.clone(), this.timeScale.clone());", + " }", + " reset() {", + " }", + "}", + "", + "function randomInt(a, b) {", + " return Math.floor(Math.random() * (b - a)) + a;", + "}", + "", + "const generators = [];", + "const tempV$2 = new THREE.Vector3();", + "const tempQ$2 = new THREE.Quaternion();", + "class Noise {", + " constructor(frequency, power, positionAmount = new ConstantValue(1), rotationAmount = new ConstantValue(0)) {", + " this.frequency = frequency;", + " this.power = power;", + " this.positionAmount = positionAmount;", + " this.rotationAmount = rotationAmount;", + " this.type = 'Noise';", + " this.duration = 0;", + " if (generators.length === 0) {", + " for (let i = 0; i < 100; i++) {", + " generators.push(new SimplexNoise());", + " }", + " }", + " }", + " initialize(particle) {", + " particle.lastPosNoise = new THREE.Vector3();", + " if (typeof particle.rotation === 'number') {", + " particle.lastRotNoise = 0;", + " }", + " else {", + " particle.lastRotNoise = new THREE.Quaternion();", + " }", + " particle.generatorIndex = [randomInt(0, 100), randomInt(0, 100), randomInt(0, 100), randomInt(0, 100)];", + " }", + " update(particle, _) {", + " let frequency = this.frequency.genValue(particle.age / particle.life);", + " let power = this.power.genValue(particle.age / particle.life);", + " let positionAmount = this.positionAmount.genValue(particle.age / particle.life);", + " let rotationAmount = this.rotationAmount.genValue(particle.age / particle.life);", + " if (positionAmount > 0 && particle.lastPosNoise !== undefined) {", + " particle.position.sub(particle.lastPosNoise);", + " tempV$2.set(generators[particle.generatorIndex[0]].noise2D(0, particle.age * frequency) *", + " power *", + " positionAmount, generators[particle.generatorIndex[1]].noise2D(0, particle.age * frequency) *", + " power *", + " positionAmount, generators[particle.generatorIndex[2]].noise2D(0, particle.age * frequency) *", + " power *", + " positionAmount);", + " particle.position.add(tempV$2);", + " particle.lastPosNoise.copy(tempV$2);", + " }", + " if (rotationAmount > 0 && particle.lastRotNoise !== undefined) {", + " if (typeof particle.rotation === 'number') {", + " particle.rotation -= particle.lastRotNoise;", + " particle.rotation +=", + " generators[particle.generatorIndex[3]].noise2D(0, particle.age * frequency) *", + " Math.PI *", + " power *", + " rotationAmount;", + " }", + " else {", + " particle.lastRotNoise.invert();", + " particle.rotation.multiply(particle.lastRotNoise);", + " tempQ$2", + " .set(generators[particle.generatorIndex[0]].noise2D(0, particle.age * frequency) *", + " power *", + " rotationAmount, generators[particle.generatorIndex[1]].noise2D(0, particle.age * frequency) *", + " power *", + " rotationAmount, generators[particle.generatorIndex[2]].noise2D(0, particle.age * frequency) *", + " power *", + " rotationAmount, generators[particle.generatorIndex[3]].noise2D(0, particle.age * frequency) *", + " power *", + " rotationAmount)", + " .normalize();", + " particle.rotation.multiply(tempQ$2);", + " particle.lastRotNoise.copy(tempQ$2);", + " }", + " }", + " }", + " toJSON() {", + " return {", + " type: this.type,", + " frequency: this.frequency.toJSON(),", + " power: this.power.toJSON(),", + " positionAmount: this.positionAmount.toJSON(),", + " rotationAmount: this.rotationAmount.toJSON(),", + " };", + " }", + " frameUpdate(delta) {", + " this.duration += delta;", + " }", + " static fromJSON(json) {", + " return new Noise(ValueGeneratorFromJSON(json.frequency), ValueGeneratorFromJSON(json.power), ValueGeneratorFromJSON(json.positionAmount), ValueGeneratorFromJSON(json.rotationAmount));", + " }", + " clone() {", + " return new Noise(this.frequency.clone(), this.power.clone(), this.positionAmount.clone(), this.rotationAmount.clone());", + " }", + " reset() { }", + "}", + "", + "class TextureSequencer {", + " constructor(scaleX = 0, scaleY = 0, position = new THREE.Vector3()) {", + " this.scaleX = scaleX;", + " this.scaleY = scaleY;", + " this.position = position;", + " this.locations = [];", + " }", + " transform(position, index) {", + " position.x = this.locations[index % this.locations.length].x * this.scaleX + this.position.x;", + " position.y = this.locations[index % this.locations.length].y * this.scaleY + this.position.y;", + " position.z = this.position.z;", + " }", + " static fromJSON(json) {", + " const textureSequencer = new TextureSequencer(json.scaleX, json.scaleY, new THREE.Vector3(json.position[0], json.position[1], json.position[2]));", + " textureSequencer.locations = json.locations.map((loc) => new THREE.Vector2(loc.x, loc.y));", + " return textureSequencer;", + " }", + " clone() {", + " const textureSequencer = new TextureSequencer(this.scaleX, this.scaleY, this.position.clone());", + " textureSequencer.locations = this.locations.map(loc => loc.clone());", + " return textureSequencer;", + " }", + " toJSON() {", + " return {", + " scaleX: this.scaleX,", + " scaleY: this.scaleY,", + " position: this.position,", + " locations: this.locations.map(loc => ({", + " x: loc.x,", + " y: loc.y,", + " }))", + " };", + " }", + " fromImage(img, threshold) {", + " const canvas = document.createElement(\"canvas\");", + " canvas.width = img.width;", + " canvas.height = img.height;", + " const ctx = canvas.getContext(\"2d\");", + " if (!ctx) {", + " return;", + " }", + " ctx.drawImage(img, 0, 0);", + " const data = ctx.getImageData(0, 0, canvas.width, canvas.height, { colorSpace: \"srgb\" });", + " canvas.remove();", + " this.locations.length = 0;", + " for (let i = 0; i < data.height; i++) {", + " for (let j = 0; j < data.width; j++) {", + " if (data.data[(i * data.width + j) * 4 + 3] > threshold) {", + " this.locations.push(new THREE.Vector2(j, data.height - i));", + " }", + " }", + " }", + " }", + "}", + "", + "function SequencerFromJSON(json) {", + " switch (json.type) {", + " case 'TextureSequencer':", + " return TextureSequencer.fromJSON(json);", + " default:", + " return new TextureSequencer();", + " }", + "}", + "", + "class ApplySequences {", + " constructor(delayBetweenParticles) {", + " this.type = 'ApplySequences';", + " this.sequencers = [];", + " this.time = 0;", + " this.index = 0;", + " this.pCount = 0;", + " this.tempV = new THREE.Vector3();", + " this.delay = delayBetweenParticles;", + " }", + " initialize(particle) {", + " particle.id = this.pCount;", + " particle.dst = new THREE.Vector3();", + " particle.begin = new THREE.Vector3();", + " particle.inMotion = false;", + " this.pCount++;", + " }", + " reset() {", + " this.time = 0;", + " this.index = 0;", + " this.pCount = 0;", + " }", + " update(particle, delta) {", + " const sequencer = this.sequencers[this.index];", + " const delay = particle.id * this.delay;", + " if (this.time >= sequencer[0].a + delay && this.time <= sequencer[0].b + delay) {", + " if (!particle.inMotion) {", + " particle.inMotion = true;", + " particle.begin.copy(particle.position);", + " sequencer[1].transform(particle.dst, particle.id);", + " }", + " particle.position.lerpVectors(particle.begin, particle.dst, ApplySequences.BEZIER.genValue((this.time - sequencer[0].a - delay) / (sequencer[0].b - sequencer[0].a)));", + " }", + " else if (this.time > sequencer[0].b + delay) {", + " particle.inMotion = false;", + " }", + " }", + " frameUpdate(delta) {", + " while (this.index + 1 < this.sequencers.length && this.time >= this.sequencers[this.index + 1][0].a) {", + " this.index++;", + " }", + " this.time += delta;", + " }", + " appendSequencer(range, sequencer) {", + " this.sequencers.push([range, sequencer]);", + " }", + " toJSON() {", + " return {", + " type: this.type,", + " delay: this.delay,", + " sequencers: this.sequencers.map(([range, sequencer]) => ({", + " range: range.toJSON(),", + " sequencer: sequencer.toJSON(),", + " })),", + " };", + " }", + " static fromJSON(json) {", + " const seq = new ApplySequences(json.delay);", + " json.sequencers.forEach((sequencerJson) => {", + " seq.sequencers.push([ValueGeneratorFromJSON(sequencerJson.range), SequencerFromJSON(sequencerJson.sequencer)]);", + " });", + " return seq;", + " }", + " clone() {", + " const applySequences = new ApplySequences(this.delay);", + " applySequences.sequencers = this.sequencers.map(seq => [seq[0].clone(), seq[1].clone()]);", + " return applySequences;", + " }", + "}", + "ApplySequences.BEZIER = new Bezier(0, 0, 1, 1);", + "", + "let physicsResolver;", + "function setPhysicsResolver(resolver) {", + " physicsResolver = resolver;", + "}", + "function getPhysicsResolver() {", + " return physicsResolver;", + "}", + "class ApplyCollision {", + " constructor(resolver, bounce) {", + " this.resolver = resolver;", + " this.bounce = bounce;", + " this.type = 'ApplyCollision';", + " this.tempV = new THREE.Vector3();", + " }", + " initialize(particle) {", + " }", + " update(particle, delta) {", + " if (this.resolver.resolve(particle.position, this.tempV)) {", + " particle.velocity.reflect(this.tempV).multiplyScalar(this.bounce);", + " }", + " }", + " frameUpdate(delta) {", + " }", + " toJSON() {", + " return {", + " type: this.type,", + " bounce: this.bounce,", + " };", + " }", + " static fromJSON(json) {", + " return new ApplyCollision(getPhysicsResolver(), json.bounce);", + " }", + " clone() {", + " return new ApplyCollision(this.resolver, this.bounce);", + " }", + " reset() {", + " }", + "}", + "", + "class ColorBySpeed {", + " constructor(color, speedRange) {", + " this.color = color;", + " this.speedRange = speedRange;", + " this.type = 'ColorBySpeed';", + " }", + " initialize(particle) {", + " if (this.color.type === 'memorizedFunction') {", + " particle.colorOverLifeMemory = {};", + " this.color.startGen(particle.colorOverLifeMemory);", + " }", + " }", + " update(particle, delta) {", + " const t = (particle.startSpeed - this.speedRange.a) / (this.speedRange.b - this.speedRange.a);", + " if (this.color.type === 'memorizedFunction') {", + " this.color.genColor(particle.color, t, particle.colorOverLifeMemory);", + " }", + " else {", + " this.color.genColor(particle.color, t);", + " }", + " particle.color.x *= particle.startColor.x;", + " particle.color.y *= particle.startColor.y;", + " particle.color.z *= particle.startColor.z;", + " particle.color.w *= particle.startColor.w;", + " }", + " frameUpdate(delta) { }", + " toJSON() {", + " return {", + " type: this.type,", + " color: this.color.toJSON(),", + " speedRange: this.speedRange.toJSON(),", + " };", + " }", + " static fromJSON(json) {", + " return new ColorBySpeed(ColorGeneratorFromJSON(json.color), IntervalValue.fromJSON(json.speedRange));", + " }", + " clone() {", + " return new ColorBySpeed(this.color.clone(), this.speedRange.clone());", + " }", + " reset() { }", + "}", + "", + "class SizeBySpeed {", + " initialize(particle) { }", + " constructor(size, speedRange) {", + " this.size = size;", + " this.speedRange = speedRange;", + " this.type = 'SizeBySpeed';", + " }", + " update(particle) {", + " const t = (particle.startSpeed - this.speedRange.a) / (this.speedRange.b - this.speedRange.a);", + " particle.size = particle.startSize * this.size.genValue(t);", + " }", + " toJSON() {", + " return {", + " type: this.type,", + " size: this.size.toJSON(),", + " speedRange: this.speedRange.toJSON(),", + " };", + " }", + " static fromJSON(json) {", + " return new SizeBySpeed(ValueGeneratorFromJSON(json.size), IntervalValue.fromJSON(json.speedRange));", + " }", + " frameUpdate(delta) { }", + " clone() {", + " return new SizeBySpeed(this.size.clone(), this.speedRange.clone());", + " }", + " reset() { }", + "}", + "", + "class RotationBySpeed {", + " constructor(angularVelocity, speedRange) {", + " this.angularVelocity = angularVelocity;", + " this.speedRange = speedRange;", + " this.type = 'RotationBySpeed';", + " this.tempQuat = new THREE.Quaternion();", + " }", + " initialize(particle) { }", + " update(particle, delta) {", + " const t = (particle.startSpeed - this.speedRange.a) / (this.speedRange.b - this.speedRange.a);", + " particle.rotation += delta * this.angularVelocity.genValue(t);", + " }", + " toJSON() {", + " return {", + " type: this.type,", + " angularVelocity: this.angularVelocity.toJSON(),", + " speedRange: this.speedRange.toJSON(),", + " };", + " }", + " static fromJSON(json) {", + " return new RotationBySpeed(ValueGeneratorFromJSON(json.angularVelocity), IntervalValue.fromJSON(json.speedRange));", + " }", + " frameUpdate(delta) { }", + " clone() {", + " return new RotationBySpeed(this.angularVelocity.clone(), this.speedRange.clone());", + " }", + " reset() { }", + "}", + "", + "class LimitSpeedOverLife {", + " initialize(particle) { }", + " constructor(speed, dampen) {", + " this.speed = speed;", + " this.dampen = dampen;", + " this.type = 'LimitSpeedOverLife';", + " }", + " update(particle, delta) {", + " let speed = particle.velocity.length();", + " let limit = this.speed.genValue(particle.age / particle.life);", + " if (speed > limit) {", + " const percent = (speed - limit) / speed;", + " particle.velocity.multiplyScalar(1 - percent * this.dampen * delta * 20);", + " }", + " }", + " toJSON() {", + " return {", + " type: this.type,", + " speed: this.speed.toJSON(),", + " dampen: this.dampen,", + " };", + " }", + " static fromJSON(json) {", + " return new LimitSpeedOverLife(ValueGeneratorFromJSON(json.speed), json.dampen);", + " }", + " frameUpdate(delta) { }", + " clone() {", + " return new LimitSpeedOverLife(this.speed.clone(), this.dampen);", + " }", + " reset() { }", + "}", + "", + "const BehaviorTypes = {", + " ApplyForce: {", + " type: 'ApplyForce',", + " constructor: ApplyForce,", + " params: [", + " ['direction', 'vec3'],", + " ['magnitude', 'value'],", + " ],", + " loadJSON: ApplyForce.fromJSON,", + " },", + " Noise: {", + " type: 'Noise',", + " constructor: Noise,", + " params: [", + " ['frequency', 'value'],", + " ['power', 'value'],", + " ['positionAmount', 'value'],", + " ['rotationAmount', 'value'],", + " ],", + " loadJSON: Noise.fromJSON,", + " },", + " TurbulenceField: {", + " type: 'TurbulenceField',", + " constructor: TurbulenceField,", + " params: [", + " ['scale', 'vec3'],", + " ['octaves', 'number'],", + " ['velocityMultiplier', 'vec3'],", + " ['timeScale', 'vec3'],", + " ],", + " loadJSON: TurbulenceField.fromJSON,", + " },", + " GravityForce: {", + " type: 'GravityForce',", + " constructor: GravityForce,", + " params: [", + " ['center', 'vec3'],", + " ['magnitude', 'number'],", + " ],", + " loadJSON: GravityForce.fromJSON,", + " },", + " ColorOverLife: {", + " type: 'ColorOverLife',", + " constructor: ColorOverLife,", + " params: [['color', 'colorFunc']],", + " loadJSON: ColorOverLife.fromJSON,", + " },", + " RotationOverLife: {", + " type: 'RotationOverLife',", + " constructor: RotationOverLife,", + " params: [['angularVelocity', 'valueFunc']],", + " loadJSON: RotationOverLife.fromJSON,", + " },", + " Rotation3DOverLife: {", + " type: 'Rotation3DOverLife',", + " constructor: Rotation3DOverLife,", + " params: [['angularVelocity', 'rotationFunc']],", + " loadJSON: Rotation3DOverLife.fromJSON,", + " },", + " SizeOverLife: {", + " type: 'SizeOverLife',", + " constructor: SizeOverLife,", + " params: [['size', 'valueFunc']],", + " loadJSON: SizeOverLife.fromJSON,", + " },", + " ColorBySpeed: {", + " type: 'ColorBySpeed',", + " constructor: ColorBySpeed,", + " params: [", + " ['color', 'colorFunc'],", + " ['speedRange', 'range'],", + " ],", + " loadJSON: ColorBySpeed.fromJSON,", + " },", + " RotationBySpeed: {", + " type: 'RotationBySpeed',", + " constructor: RotationBySpeed,", + " params: [", + " ['angularVelocity', 'valueFunc'],", + " ['speedRange', 'range'],", + " ],", + " loadJSON: RotationBySpeed.fromJSON,", + " },", + " SizeBySpeed: {", + " type: 'SizeBySpeed',", + " constructor: SizeBySpeed,", + " params: [", + " ['size', 'valueFunc'],", + " ['speedRange', 'range'],", + " ],", + " loadJSON: SizeBySpeed.fromJSON,", + " },", + " SpeedOverLife: {", + " type: 'SpeedOverLife',", + " constructor: SpeedOverLife,", + " params: [['speed', 'valueFunc']],", + " loadJSON: SpeedOverLife.fromJSON,", + " },", + " FrameOverLife: {", + " type: 'FrameOverLife',", + " constructor: FrameOverLife,", + " params: [['frame', 'valueFunc']],", + " loadJSON: FrameOverLife.fromJSON,", + " },", + " ForceOverLife: {", + " type: 'ForceOverLife',", + " constructor: ForceOverLife,", + " params: [", + " ['x', 'valueFunc'],", + " ['y', 'valueFunc'],", + " ['z', 'valueFunc'],", + " ],", + " loadJSON: ForceOverLife.fromJSON,", + " },", + " OrbitOverLife: {", + " type: 'OrbitOverLife',", + " constructor: OrbitOverLife,", + " params: [", + " ['orbitSpeed', 'valueFunc'],", + " ['axis', 'vec3'],", + " ],", + " loadJSON: OrbitOverLife.fromJSON,", + " },", + " WidthOverLength: {", + " type: 'WidthOverLength',", + " constructor: WidthOverLength,", + " params: [['width', 'valueFunc']],", + " loadJSON: WidthOverLength.fromJSON,", + " },", + " ChangeEmitDirection: {", + " type: 'ChangeEmitDirection',", + " constructor: ChangeEmitDirection,", + " params: [['angle', 'value']],", + " loadJSON: ChangeEmitDirection.fromJSON,", + " },", + " EmitSubParticleSystem: {", + " type: 'EmitSubParticleSystem',", + " constructor: EmitSubParticleSystem,", + " params: [", + " ['particleSystem', 'self'],", + " ['useVelocityAsBasis', 'boolean'],", + " ['subParticleSystem', 'particleSystem'],", + " ['mode', 'number'],", + " ['emitProbability', 'number'],", + " ],", + " loadJSON: EmitSubParticleSystem.fromJSON,", + " },", + " LimitSpeedOverLife: {", + " type: 'LimitSpeedOverLife',", + " constructor: LimitSpeedOverLife,", + " params: [", + " ['speed', 'valueFunc'],", + " ['dampen', 'number'],", + " ],", + " loadJSON: LimitSpeedOverLife.fromJSON,", + " },", + "};", + "function BehaviorFromJSON(json, particleSystem) {", + " return BehaviorTypes[json.type].loadJSON(json, particleSystem);", + "}", + "", + "let EmitterMode = void 0;", + "(function (EmitterMode) {", + " EmitterMode[EmitterMode[\"Random\"] = 0] = \"Random\";", + " EmitterMode[EmitterMode[\"Loop\"] = 1] = \"Loop\";", + " EmitterMode[EmitterMode[\"PingPong\"] = 2] = \"PingPong\";", + " EmitterMode[EmitterMode[\"Burst\"] = 3] = \"Burst\";", + "})(EmitterMode || (EmitterMode = {}));", + "function getValueFromEmitterMode(mode, currentValue, spread) {", + " let u;", + " if (EmitterMode.Random === mode) {", + " currentValue = Math.random();", + " }", + " if (spread > 0) {", + " u = Math.floor(currentValue / spread) * spread;", + " }", + " else {", + " u = currentValue;", + " }", + " switch (mode) {", + " case EmitterMode.Loop:", + " u = u % 1;", + " break;", + " case EmitterMode.PingPong:", + " u = Math.abs((u % 2) - 1);", + " break;", + " }", + " return u;", + "}", + "", + "class ConeEmitter {", + " constructor(parameters = {}) {", + " var _a, _b, _c, _d, _e, _f, _g;", + " this.type = 'cone';", + " this.currentValue = 0;", + " this.radius = (_a = parameters.radius) !== null && _a !== void 0 ? _a : 10;", + " this.arc = (_b = parameters.arc) !== null && _b !== void 0 ? _b : 2.0 * Math.PI;", + " this.thickness = (_c = parameters.thickness) !== null && _c !== void 0 ? _c : 1;", + " this.angle = (_d = parameters.angle) !== null && _d !== void 0 ? _d : Math.PI / 6;", + " this.mode = (_e = parameters.mode) !== null && _e !== void 0 ? _e : EmitterMode.Random;", + " this.spread = (_f = parameters.spread) !== null && _f !== void 0 ? _f : 0;", + " this.speed = (_g = parameters.speed) !== null && _g !== void 0 ? _g : new ConstantValue(1);", + " }", + " update(system, delta) {", + " if (EmitterMode.Random != this.mode) {", + " this.currentValue += this.speed.genValue(system.emissionState.time / system.duration) * delta;", + " }", + " }", + " initialize(p) {", + " const u = getValueFromEmitterMode(this.mode, this.currentValue, this.spread);", + " const rand = THREE.MathUtils.lerp(1 - this.thickness, 1, Math.random());", + " const theta = u * this.arc;", + " const r = Math.sqrt(rand);", + " const sinTheta = Math.sin(theta);", + " const cosTheta = Math.cos(theta);", + " p.position.x = r * cosTheta;", + " p.position.y = r * sinTheta;", + " p.position.z = 0;", + " const angle = this.angle * r;", + " p.velocity.set(0, 0, Math.cos(angle)).addScaledVector(p.position, Math.sin(angle)).multiplyScalar(p.startSpeed);", + " p.position.multiplyScalar(this.radius);", + " }", + " toJSON() {", + " return {", + " type: 'cone',", + " radius: this.radius,", + " arc: this.arc,", + " thickness: this.thickness,", + " angle: this.angle,", + " mode: this.mode,", + " spread: this.spread,", + " speed: this.speed.toJSON(),", + " };", + " }", + " static fromJSON(json) {", + " return new ConeEmitter({", + " radius: json.radius,", + " arc: json.arc,", + " thickness: json.thickness,", + " angle: json.angle,", + " mode: json.mode,", + " speed: json.speed ? ValueGeneratorFromJSON(json.speed) : undefined,", + " spread: json.spread,", + " });", + " }", + " clone() {", + " return new ConeEmitter({", + " radius: this.radius,", + " arc: this.arc,", + " thickness: this.thickness,", + " angle: this.angle,", + " mode: this.mode,", + " speed: this.speed.clone(),", + " spread: this.spread,", + " });", + " }", + "}", + "", + "class CircleEmitter {", + " constructor(parameters = {}) {", + " var _a, _b, _c, _d, _e, _f;", + " this.type = 'circle';", + " this.currentValue = 0;", + " this.radius = (_a = parameters.radius) !== null && _a !== void 0 ? _a : 10;", + " this.arc = (_b = parameters.arc) !== null && _b !== void 0 ? _b : 2.0 * Math.PI;", + " this.thickness = (_c = parameters.thickness) !== null && _c !== void 0 ? _c : 1;", + " this.mode = (_d = parameters.mode) !== null && _d !== void 0 ? _d : EmitterMode.Random;", + " this.spread = (_e = parameters.spread) !== null && _e !== void 0 ? _e : 0;", + " this.speed = (_f = parameters.speed) !== null && _f !== void 0 ? _f : new ConstantValue(1);", + " }", + " update(system, delta) {", + " this.currentValue += this.speed.genValue(system.emissionState.time / system.duration) * delta;", + " }", + " initialize(p) {", + " const u = getValueFromEmitterMode(this.mode, this.currentValue, this.spread);", + " const r = THREE.MathUtils.lerp(1 - this.thickness, 1, Math.random());", + " const theta = u * this.arc;", + " p.position.x = Math.cos(theta);", + " p.position.y = Math.sin(theta);", + " p.position.z = 0;", + " p.velocity.copy(p.position).multiplyScalar(p.startSpeed);", + " p.position.multiplyScalar(this.radius * r);", + " }", + " toJSON() {", + " return {", + " type: 'circle',", + " radius: this.radius,", + " arc: this.arc,", + " thickness: this.thickness,", + " mode: this.mode,", + " spread: this.spread,", + " speed: this.speed.toJSON(),", + " };", + " }", + " static fromJSON(json) {", + " return new CircleEmitter({", + " radius: json.radius,", + " arc: json.arc,", + " thickness: json.thickness,", + " mode: json.mode,", + " speed: json.speed ? ValueGeneratorFromJSON(json.speed) : undefined,", + " spread: json.spread,", + " });", + " }", + " clone() {", + " return new CircleEmitter({", + " radius: this.radius,", + " arc: this.arc,", + " thickness: this.thickness,", + " mode: this.mode,", + " speed: this.speed.clone(),", + " spread: this.spread,", + " });", + " }", + "}", + "", + "class DonutEmitter {", + " constructor(parameters = {}) {", + " var _a, _b, _c, _d, _e, _f, _g;", + " this.type = 'donut';", + " this.currentValue = 0;", + " this.radius = (_a = parameters.radius) !== null && _a !== void 0 ? _a : 10;", + " this.arc = (_b = parameters.arc) !== null && _b !== void 0 ? _b : 2.0 * Math.PI;", + " this.thickness = (_c = parameters.thickness) !== null && _c !== void 0 ? _c : 1;", + " this.donutRadius = (_d = parameters.donutRadius) !== null && _d !== void 0 ? _d : this.radius * 0.2;", + " this.mode = (_e = parameters.mode) !== null && _e !== void 0 ? _e : EmitterMode.Random;", + " this.spread = (_f = parameters.spread) !== null && _f !== void 0 ? _f : 0;", + " this.speed = (_g = parameters.speed) !== null && _g !== void 0 ? _g : new ConstantValue(1);", + " }", + " update(system, delta) {", + " if (EmitterMode.Random != this.mode) {", + " this.currentValue += this.speed.genValue(system.emissionState.time / system.duration) * delta;", + " }", + " }", + " initialize(p) {", + " const u = getValueFromEmitterMode(this.mode, this.currentValue, this.spread);", + " const v = Math.random();", + " const rand = THREE.MathUtils.lerp(1 - this.thickness, 1, Math.random());", + " const theta = u * this.arc;", + " const phi = v * Math.PI * 2;", + " const sinTheta = Math.sin(theta);", + " const cosTheta = Math.cos(theta);", + " p.position.x = this.radius * cosTheta;", + " p.position.y = this.radius * sinTheta;", + " p.position.z = 0;", + " p.velocity.z = this.donutRadius * rand * Math.sin(phi);", + " p.velocity.x = this.donutRadius * rand * Math.cos(phi) * cosTheta;", + " p.velocity.y = this.donutRadius * rand * Math.cos(phi) * sinTheta;", + " p.position.add(p.velocity);", + " p.velocity.normalize().multiplyScalar(p.startSpeed);", + " }", + " toJSON() {", + " return {", + " type: 'donut',", + " radius: this.radius,", + " arc: this.arc,", + " thickness: this.thickness,", + " donutRadius: this.donutRadius,", + " mode: this.mode,", + " spread: this.spread,", + " speed: this.speed.toJSON(),", + " };", + " }", + " static fromJSON(json) {", + " return new DonutEmitter({", + " radius: json.radius,", + " arc: json.arc,", + " thickness: json.thickness,", + " donutRadius: json.donutRadius,", + " mode: json.mode,", + " speed: json.speed ? ValueGeneratorFromJSON(json.speed) : undefined,", + " spread: json.spread,", + " });", + " }", + " clone() {", + " return new DonutEmitter({", + " radius: this.radius,", + " arc: this.arc,", + " thickness: this.thickness,", + " donutRadius: this.donutRadius,", + " mode: this.mode,", + " speed: this.speed.clone(),", + " spread: this.spread,", + " });", + " }", + "}", + "", + "class PointEmitter {", + " constructor() {", + " this.type = 'point';", + " }", + " update(system, delta) { }", + " initialize(p) {", + " const u = Math.random();", + " const v = Math.random();", + " const theta = u * Math.PI * 2;", + " const phi = Math.acos(2.0 * v - 1.0);", + " const r = Math.cbrt(Math.random());", + " const sinTheta = Math.sin(theta);", + " const cosTheta = Math.cos(theta);", + " const sinPhi = Math.sin(phi);", + " const cosPhi = Math.cos(phi);", + " p.velocity.x = r * sinPhi * cosTheta;", + " p.velocity.y = r * sinPhi * sinTheta;", + " p.velocity.z = r * cosPhi;", + " p.velocity.multiplyScalar(p.startSpeed);", + " p.position.setScalar(0);", + " }", + " toJSON() {", + " return {", + " type: 'point',", + " };", + " }", + " static fromJSON(json) {", + " return new PointEmitter();", + " }", + " clone() {", + " return new PointEmitter();", + " }", + "}", + "", + "class SphereEmitter {", + " constructor(parameters = {}) {", + " var _a, _b, _c, _d, _e, _f;", + " this.type = 'sphere';", + " this.currentValue = 0;", + " this.radius = (_a = parameters.radius) !== null && _a !== void 0 ? _a : 10;", + " this.arc = (_b = parameters.arc) !== null && _b !== void 0 ? _b : 2.0 * Math.PI;", + " this.thickness = (_c = parameters.thickness) !== null && _c !== void 0 ? _c : 1;", + " this.mode = (_d = parameters.mode) !== null && _d !== void 0 ? _d : EmitterMode.Random;", + " this.spread = (_e = parameters.spread) !== null && _e !== void 0 ? _e : 0;", + " this.speed = (_f = parameters.speed) !== null && _f !== void 0 ? _f : new ConstantValue(1);", + " }", + " update(system, delta) {", + " if (EmitterMode.Random != this.mode) {", + " this.currentValue += this.speed.genValue(system.emissionState.time / system.duration) * delta;", + " }", + " }", + " initialize(p) {", + " const u = getValueFromEmitterMode(this.mode, this.currentValue, this.spread);", + " const v = Math.random();", + " const rand = THREE.MathUtils.lerp(1 - this.thickness, 1, Math.random());", + " const theta = u * this.arc;", + " const phi = Math.acos(2.0 * v - 1.0);", + " const sinTheta = Math.sin(theta);", + " const cosTheta = Math.cos(theta);", + " const sinPhi = Math.sin(phi);", + " const cosPhi = Math.cos(phi);", + " p.position.x = sinPhi * cosTheta;", + " p.position.y = sinPhi * sinTheta;", + " p.position.z = cosPhi;", + " p.velocity.copy(p.position).multiplyScalar(p.startSpeed);", + " p.position.multiplyScalar(this.radius * rand);", + " }", + " toJSON() {", + " return {", + " type: 'sphere',", + " radius: this.radius,", + " arc: this.arc,", + " thickness: this.thickness,", + " mode: this.mode,", + " spread: this.spread,", + " speed: this.speed.toJSON(),", + " };", + " }", + " static fromJSON(json) {", + " return new SphereEmitter({", + " radius: json.radius,", + " arc: json.arc,", + " thickness: json.thickness,", + " mode: json.mode,", + " speed: json.speed ? ValueGeneratorFromJSON(json.speed) : undefined,", + " spread: json.spread,", + " });", + " }", + " clone() {", + " return new SphereEmitter({", + " radius: this.radius,", + " arc: this.arc,", + " thickness: this.thickness,", + " mode: this.mode,", + " speed: this.speed.clone(),", + " spread: this.spread,", + " });", + " }", + "}", + "", + "class HemisphereEmitter {", + " constructor(parameters = {}) {", + " var _a, _b, _c, _d, _e, _f;", + " this.type = 'sphere';", + " this.currentValue = 0;", + " this.radius = (_a = parameters.radius) !== null && _a !== void 0 ? _a : 10;", + " this.arc = (_b = parameters.arc) !== null && _b !== void 0 ? _b : 2.0 * Math.PI;", + " this.thickness = (_c = parameters.thickness) !== null && _c !== void 0 ? _c : 1;", + " this.mode = (_d = parameters.mode) !== null && _d !== void 0 ? _d : EmitterMode.Random;", + " this.spread = (_e = parameters.spread) !== null && _e !== void 0 ? _e : 0;", + " this.speed = (_f = parameters.speed) !== null && _f !== void 0 ? _f : new ConstantValue(1);", + " }", + " update(system, delta) {", + " if (EmitterMode.Random != this.mode) {", + " this.currentValue += this.speed.genValue(system.emissionState.time / system.duration) * delta;", + " }", + " }", + " initialize(p) {", + " const u = getValueFromEmitterMode(this.mode, this.currentValue, this.spread);", + " const v = Math.random();", + " const rand = THREE.MathUtils.lerp(1 - this.thickness, 1, Math.random());", + " const theta = u * this.arc;", + " const phi = Math.acos(v);", + " const sinTheta = Math.sin(theta);", + " const cosTheta = Math.cos(theta);", + " const sinPhi = Math.sin(phi);", + " const cosPhi = Math.cos(phi);", + " p.position.x = sinPhi * cosTheta;", + " p.position.y = sinPhi * sinTheta;", + " p.position.z = cosPhi;", + " p.velocity.copy(p.position).multiplyScalar(p.startSpeed);", + " p.position.multiplyScalar(this.radius * rand);", + " }", + " toJSON() {", + " return {", + " type: 'hemisphere',", + " radius: this.radius,", + " arc: this.arc,", + " thickness: this.thickness,", + " mode: this.mode,", + " spread: this.spread,", + " speed: this.speed.toJSON(),", + " };", + " }", + " static fromJSON(json) {", + " return new HemisphereEmitter({", + " radius: json.radius,", + " arc: json.arc,", + " thickness: json.thickness,", + " mode: json.mode,", + " speed: json.speed ? ValueGeneratorFromJSON(json.speed) : undefined,", + " spread: json.spread,", + " });", + " }", + " clone() {", + " return new HemisphereEmitter({", + " radius: this.radius,", + " arc: this.arc,", + " thickness: this.thickness,", + " mode: this.mode,", + " speed: this.speed.clone(),", + " spread: this.spread,", + " });", + " }", + "}", + "", + "class GridEmitter {", + " constructor(parameters = {}) {", + " var _a, _b, _c, _d;", + " this.type = 'grid';", + " this.width = (_a = parameters.width) !== null && _a !== void 0 ? _a : 1;", + " this.height = (_b = parameters.height) !== null && _b !== void 0 ? _b : 1;", + " this.column = (_c = parameters.column) !== null && _c !== void 0 ? _c : 10;", + " this.row = (_d = parameters.row) !== null && _d !== void 0 ? _d : 10;", + " }", + " initialize(p) {", + " const r = Math.floor(Math.random() * this.row);", + " const c = Math.floor(Math.random() * this.column);", + " p.position.x = (c * this.width) / this.column - this.width / 2;", + " p.position.y = (r * this.height) / this.row - this.height / 2;", + " p.position.z = 0;", + " p.velocity.set(0, 0, p.startSpeed);", + " }", + " toJSON() {", + " return {", + " type: 'grid',", + " width: this.width,", + " height: this.height,", + " column: this.column,", + " row: this.row,", + " };", + " }", + " static fromJSON(json) {", + " return new GridEmitter(json);", + " }", + " clone() {", + " return new GridEmitter({", + " width: this.width,", + " height: this.height,", + " column: this.column,", + " row: this.row,", + " });", + " }", + " update(system, delta) { }", + "}", + "", + "class MeshSurfaceEmitter {", + " get geometry() {", + " return this._geometry;", + " }", + " set geometry(geometry) {", + " this._geometry = geometry;", + " if (geometry === undefined) {", + " return;", + " }", + " if (typeof geometry === 'string') {", + " return;", + " }", + " const tri = new THREE.Triangle();", + " this._triangleIndexToArea.length = 0;", + " let area = 0;", + " if (!geometry.getIndex()) {", + " return;", + " }", + " const array = geometry.getIndex().array;", + " const triCount = array.length / 3;", + " this._triangleIndexToArea.push(0);", + " for (let i = 0; i < triCount; i++) {", + " tri.setFromAttributeAndIndices(geometry.getAttribute('position'), array[i * 3], array[i * 3 + 1], array[i * 3 + 2]);", + " area += tri.getArea();", + " this._triangleIndexToArea.push(area);", + " }", + " geometry.userData.triangleIndexToArea = this._triangleIndexToArea;", + " }", + " constructor(geometry) {", + " this.type = 'mesh_surface';", + " this._triangleIndexToArea = [];", + " this._tempA = new THREE.Vector3();", + " this._tempB = new THREE.Vector3();", + " this._tempC = new THREE.Vector3();", + " if (!geometry) {", + " return;", + " }", + " this.geometry = geometry;", + " }", + " initialize(p) {", + " const geometry = this._geometry;", + " if (!geometry || geometry.getIndex() === null) {", + " p.position.set(0, 0, 0);", + " p.velocity.set(0, 0, 1).multiplyScalar(p.startSpeed);", + " return;", + " }", + " const triCount = this._triangleIndexToArea.length - 1;", + " let left = 0, right = triCount;", + " const target = Math.random() * this._triangleIndexToArea[triCount];", + " while (left + 1 < right) {", + " const mid = Math.floor((left + right) / 2);", + " if (target < this._triangleIndexToArea[mid]) {", + " right = mid;", + " }", + " else {", + " left = mid;", + " }", + " }", + " let u1 = Math.random();", + " let u2 = Math.random();", + " if (u1 + u2 > 1) {", + " u1 = 1 - u1;", + " u2 = 1 - u2;", + " }", + " const index1 = geometry.getIndex().array[left * 3];", + " const index2 = geometry.getIndex().array[left * 3 + 1];", + " const index3 = geometry.getIndex().array[left * 3 + 2];", + " const positionBuffer = geometry.getAttribute('position');", + " this._tempA.fromBufferAttribute(positionBuffer, index1);", + " this._tempB.fromBufferAttribute(positionBuffer, index2);", + " this._tempC.fromBufferAttribute(positionBuffer, index3);", + " this._tempB.sub(this._tempA);", + " this._tempC.sub(this._tempA);", + " this._tempA.addScaledVector(this._tempB, u1).addScaledVector(this._tempC, u2);", + " p.position.copy(this._tempA);", + " this._tempA.copy(this._tempB).cross(this._tempC).normalize();", + " p.velocity.copy(this._tempA).normalize().multiplyScalar(p.startSpeed);", + " }", + " toJSON() {", + " return {", + " type: 'mesh_surface',", + " mesh: this._geometry ? this._geometry.uuid : '',", + " };", + " }", + " static fromJSON(json, meta) {", + " return new MeshSurfaceEmitter(meta.geometries[json.geometry]);", + " }", + " clone() {", + " return new MeshSurfaceEmitter(this._geometry);", + " }", + " update(system, delta) { }", + "}", + "", + "const EmitterShapes = {", + " circle: {", + " type: 'circle',", + " params: [", + " ['radius', 'number'],", + " ['arc', 'radian'],", + " ['thickness', 'number'],", + " ['mode', 'emitterMode'],", + " ['spread', 'number'],", + " ['speed', 'valueFunc'],", + " ],", + " constructor: CircleEmitter,", + " loadJSON: CircleEmitter.fromJSON,", + " },", + " cone: {", + " type: 'cone',", + " params: [", + " ['radius', 'number'],", + " ['arc', 'radian'],", + " ['thickness', 'number'],", + " ['angle', 'radian'],", + " ['mode', 'emitterMode'],", + " ['spread', 'number'],", + " ['speed', 'valueFunc'],", + " ],", + " constructor: ConeEmitter,", + " loadJSON: ConeEmitter.fromJSON,", + " },", + " donut: {", + " type: 'donut',", + " params: [", + " ['radius', 'number'],", + " ['arc', 'radian'],", + " ['thickness', 'number'],", + " ['donutRadius', 'number'],", + " ['mode', 'emitterMode'],", + " ['spread', 'number'],", + " ['speed', 'valueFunc'],", + " ],", + " constructor: DonutEmitter,", + " loadJSON: DonutEmitter.fromJSON,", + " },", + " point: { type: 'point', params: [], constructor: PointEmitter, loadJSON: PointEmitter.fromJSON },", + " sphere: {", + " type: 'sphere',", + " params: [", + " ['radius', 'number'],", + " ['arc', 'radian'],", + " ['thickness', 'number'],", + " ['angle', 'radian'],", + " ['mode', 'emitterMode'],", + " ['spread', 'number'],", + " ['speed', 'valueFunc'],", + " ],", + " constructor: SphereEmitter,", + " loadJSON: SphereEmitter.fromJSON,", + " },", + " hemisphere: {", + " type: 'hemisphere',", + " params: [", + " ['radius', 'number'],", + " ['arc', 'radian'],", + " ['thickness', 'number'],", + " ['angle', 'radian'],", + " ['mode', 'emitterMode'],", + " ['spread', 'number'],", + " ['speed', 'valueFunc'],", + " ],", + " constructor: HemisphereEmitter,", + " loadJSON: HemisphereEmitter.fromJSON,", + " },", + " grid: {", + " type: 'grid',", + " params: [", + " ['width', 'number'],", + " ['height', 'number'],", + " ['rows', 'number'],", + " ['column', 'number'],", + " ],", + " constructor: GridEmitter,", + " loadJSON: GridEmitter.fromJSON,", + " },", + " mesh_surface: {", + " type: 'mesh_surface',", + " params: [['geometry', 'geometry']],", + " constructor: MeshSurfaceEmitter,", + " loadJSON: MeshSurfaceEmitter.fromJSON,", + " },", + "};", + "function EmitterFromJSON(json, meta) {", + " return EmitterShapes[json.type].loadJSON(json, meta);", + "}", + "", + "let RenderMode = void 0;", + "(function (RenderMode) {", + " RenderMode[RenderMode[\"BillBoard\"] = 0] = \"BillBoard\";", + " RenderMode[RenderMode[\"StretchedBillBoard\"] = 1] = \"StretchedBillBoard\";", + " RenderMode[RenderMode[\"Mesh\"] = 2] = \"Mesh\";", + " RenderMode[RenderMode[\"Trail\"] = 3] = \"Trail\";", + " RenderMode[RenderMode[\"HorizontalBillBoard\"] = 4] = \"HorizontalBillBoard\";", + " RenderMode[RenderMode[\"VerticalBillBoard\"] = 5] = \"VerticalBillBoard\";", + "})(RenderMode || (RenderMode = {}));", + "class VFXBatch extends THREE.Mesh {", + " constructor(settings) {", + " super();", + " this.type = 'VFXBatch';", + " this.maxParticles = 1000;", + " this.systems = new Set();", + " const layers = new THREE.Layers();", + " layers.mask = settings.layers.mask;", + " const newMat = settings.material.clone();", + " newMat.defines = {};", + " Object.assign(newMat.defines, settings.material.defines);", + " this.settings = {", + " instancingGeometry: settings.instancingGeometry,", + " renderMode: settings.renderMode,", + " renderOrder: settings.renderOrder,", + " material: newMat,", + " uTileCount: settings.uTileCount,", + " vTileCount: settings.vTileCount,", + " layers: layers,", + " };", + " this.frustumCulled = false;", + " this.renderOrder = this.settings.renderOrder;", + " }", + " addSystem(system) {", + " this.systems.add(system);", + " }", + " removeSystem(system) {", + " this.systems.delete(system);", + " }", + "}", + "", + "const UP = new THREE.Vector3(0, 0, 1);", + "const tempQ$1 = new THREE.Quaternion();", + "const tempV$1 = new THREE.Vector3();", + "const tempV2$1 = new THREE.Vector3();", + "new THREE.Vector3();", + "const PREWARM_FPS$1 = 60;", + "const DEFAULT_GEOMETRY$1 = new THREE.PlaneGeometry(1, 1, 1, 1);", + "class ParticleSystem {", + " set time(time) {", + " this.emissionState.time = time;", + " }", + " get time() {", + " return this.emissionState.time;", + " }", + " get layers() {", + " return this.rendererSettings.layers;", + " }", + " get texture() {", + " return this.rendererSettings.material.map;", + " }", + " set texture(texture) {", + " this.rendererSettings.material.map = texture;", + " this.neededToUpdateRender = true;", + " }", + " get material() {", + " return this.rendererSettings.material;", + " }", + " set material(material) {", + " this.rendererSettings.material = material;", + " this.neededToUpdateRender = true;", + " }", + " get uTileCount() {", + " return this.rendererSettings.uTileCount;", + " }", + " set uTileCount(u) {", + " this.rendererSettings.uTileCount = u;", + " this.neededToUpdateRender = true;", + " }", + " get vTileCount() {", + " return this.rendererSettings.vTileCount;", + " }", + " set vTileCount(v) {", + " this.rendererSettings.vTileCount = v;", + " this.neededToUpdateRender = true;", + " }", + " get instancingGeometry() {", + " return this.rendererSettings.instancingGeometry;", + " }", + " set instancingGeometry(geometry) {", + " this.restart();", + " this.particles.length = 0;", + " this.rendererSettings.instancingGeometry = geometry;", + " this.neededToUpdateRender = true;", + " }", + " get renderMode() {", + " return this.rendererSettings.renderMode;", + " }", + " set renderMode(renderMode) {", + " if ((this.rendererSettings.renderMode != RenderMode.Trail && renderMode === RenderMode.Trail) ||", + " (this.rendererSettings.renderMode == RenderMode.Trail && renderMode !== RenderMode.Trail)) {", + " this.restart();", + " this.particles.length = 0;", + " }", + " if (this.rendererSettings.renderMode !== renderMode) {", + " switch (renderMode) {", + " case RenderMode.Trail:", + " this.rendererEmitterSettings = {", + " startLength: new ConstantValue(30),", + " followLocalOrigin: false,", + " };", + " break;", + " case RenderMode.Mesh:", + " this.rendererEmitterSettings = {", + " geometry: new THREE.PlaneGeometry(1, 1),", + " };", + " this.startRotation = new AxisAngleGenerator(new THREE.Vector3(0, 1, 0), new ConstantValue(0));", + " break;", + " case RenderMode.StretchedBillBoard:", + " this.rendererEmitterSettings = { speedFactor: 0, lengthFactor: 2 };", + " if (this.rendererSettings.renderMode === RenderMode.Mesh) {", + " this.startRotation = new ConstantValue(0);", + " }", + " break;", + " case RenderMode.BillBoard:", + " case RenderMode.VerticalBillBoard:", + " case RenderMode.HorizontalBillBoard:", + " this.rendererEmitterSettings = {};", + " if (this.rendererSettings.renderMode === RenderMode.Mesh) {", + " this.startRotation = new ConstantValue(0);", + " }", + " break;", + " }", + " }", + " this.rendererSettings.renderMode = renderMode;", + " this.neededToUpdateRender = true;", + " }", + " get renderOrder() {", + " return this.rendererSettings.renderOrder;", + " }", + " set renderOrder(renderOrder) {", + " this.rendererSettings.renderOrder = renderOrder;", + " this.neededToUpdateRender = true;", + " }", + " get blending() {", + " return this.rendererSettings.material.blending;", + " }", + " set blending(blending) {", + " this.rendererSettings.material.blending = blending;", + " this.neededToUpdateRender = true;", + " }", + " constructor(parameters) {", + " var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m, _o, _p, _q, _r, _s, _t, _u, _v, _w, _x;", + " this.temp = new THREE.Vector3();", + " this.travelDistance = 0;", + " this.normalMatrix = new THREE.Matrix3();", + " this.firstTimeUpdate = true;", + " this.autoDestroy = parameters.autoDestroy === undefined ? false : parameters.autoDestroy;", + " this.duration = (_a = parameters.duration) !== null && _a !== void 0 ? _a : 1;", + " this.looping = parameters.looping === undefined ? true : parameters.looping;", + " this.prewarm = parameters.prewarm === undefined ? false : parameters.prewarm;", + " this.startLife = (_b = parameters.startLife) !== null && _b !== void 0 ? _b : new ConstantValue(5);", + " this.startSpeed = (_c = parameters.startSpeed) !== null && _c !== void 0 ? _c : new ConstantValue(0);", + " this.startRotation = (_d = parameters.startRotation) !== null && _d !== void 0 ? _d : new ConstantValue(0);", + " this.startSize = (_e = parameters.startSize) !== null && _e !== void 0 ? _e : new ConstantValue(1);", + " this.startColor = (_f = parameters.startColor) !== null && _f !== void 0 ? _f : new ConstantColor(new THREE.Vector4(1, 1, 1, 1));", + " this.emissionOverTime = (_g = parameters.emissionOverTime) !== null && _g !== void 0 ? _g : new ConstantValue(10);", + " this.emissionOverDistance = (_h = parameters.emissionOverDistance) !== null && _h !== void 0 ? _h : new ConstantValue(0);", + " this.emissionBursts = (_j = parameters.emissionBursts) !== null && _j !== void 0 ? _j : [];", + " this.onlyUsedByOther = (_k = parameters.onlyUsedByOther) !== null && _k !== void 0 ? _k : false;", + " this.emitterShape = (_l = parameters.shape) !== null && _l !== void 0 ? _l : new SphereEmitter();", + " this.behaviors = (_m = parameters.behaviors) !== null && _m !== void 0 ? _m : new Array();", + " this.worldSpace = (_o = parameters.worldSpace) !== null && _o !== void 0 ? _o : false;", + " this.rendererEmitterSettings = (_p = parameters.rendererEmitterSettings) !== null && _p !== void 0 ? _p : {};", + " if (parameters.renderMode === RenderMode.StretchedBillBoard) {", + " const stretchedBillboardSettings = this.rendererEmitterSettings;", + " if (parameters.speedFactor !== undefined) {", + " stretchedBillboardSettings.speedFactor = parameters.speedFactor;", + " }", + " stretchedBillboardSettings.speedFactor = (_q = stretchedBillboardSettings.speedFactor) !== null && _q !== void 0 ? _q : 0;", + " stretchedBillboardSettings.lengthFactor = (_r = stretchedBillboardSettings.lengthFactor) !== null && _r !== void 0 ? _r : 0;", + " }", + " this.rendererSettings = {", + " instancingGeometry: (_s = parameters.instancingGeometry) !== null && _s !== void 0 ? _s : DEFAULT_GEOMETRY$1,", + " renderMode: (_t = parameters.renderMode) !== null && _t !== void 0 ? _t : RenderMode.BillBoard,", + " renderOrder: (_u = parameters.renderOrder) !== null && _u !== void 0 ? _u : 0,", + " material: parameters.material,", + " uTileCount: (_v = parameters.uTileCount) !== null && _v !== void 0 ? _v : 1,", + " vTileCount: (_w = parameters.vTileCount) !== null && _w !== void 0 ? _w : 1,", + " layers: (_x = parameters.layers) !== null && _x !== void 0 ? _x : new THREE.Layers(),", + " };", + " this.neededToUpdateRender = true;", + " this.particles = new Array();", + " this.startTileIndex = parameters.startTileIndex || new ConstantValue(0);", + " this.emitter = new ParticleEmitter(this);", + " this.paused = false;", + " this.particleNum = 0;", + " this.emissionState = {", + " burstIndex: 0,", + " burstWaveIndex: 0,", + " time: 0,", + " waitEmiting: 0,", + " travelDistance: 0,", + " };", + " this.emitEnded = false;", + " this.markForDestroy = false;", + " this.prewarmed = false;", + " }", + " pause() {", + " this.paused = true;", + " }", + " play() {", + " this.paused = false;", + " }", + " spawn(count, emissionState, matrix) {", + " tempQ$1.setFromRotationMatrix(matrix);", + " const translation = tempV$1;", + " const quaternion = tempQ$1;", + " const scale = tempV2$1;", + " matrix.decompose(translation, quaternion, scale);", + " for (let i = 0; i < count; i++) {", + " this.particleNum++;", + " while (this.particles.length < this.particleNum) {", + " if (this.rendererSettings.renderMode === RenderMode.Trail) {", + " this.particles.push(new TrailParticle());", + " }", + " else {", + " this.particles.push(new SpriteParticle());", + " }", + " }", + " const particle = this.particles[this.particleNum - 1];", + " particle.speedModifier = 1;", + " this.startColor.genColor(particle.startColor, this.emissionState.time, {});", + " particle.color.copy(particle.startColor);", + " particle.startSpeed = this.startSpeed.genValue(emissionState.time / this.duration);", + " particle.life = this.startLife.genValue(emissionState.time / this.duration);", + " particle.age = 0;", + " particle.startSize = this.startSize.genValue(emissionState.time / this.duration);", + " particle.uvTile = Math.floor(this.startTileIndex.genValue() + 0.001);", + " particle.size = particle.startSize;", + " if (this.rendererSettings.renderMode === RenderMode.Mesh ||", + " this.rendererSettings.renderMode === RenderMode.BillBoard ||", + " this.rendererSettings.renderMode === RenderMode.VerticalBillBoard ||", + " this.rendererSettings.renderMode === RenderMode.HorizontalBillBoard ||", + " this.rendererSettings.renderMode === RenderMode.StretchedBillBoard) {", + " const sprite = particle;", + " if (this.rendererSettings.renderMode === RenderMode.Mesh) {", + " if (!(sprite.rotation instanceof THREE.Quaternion)) {", + " sprite.rotation = new THREE.Quaternion();", + " }", + " if (this.startRotation.type === 'rotation') {", + " this.startRotation.genValue(sprite.rotation, emissionState.time / this.duration);", + " }", + " else {", + " sprite.rotation.setFromAxisAngle(UP, this.startRotation.genValue((emissionState.time / this.duration)));", + " }", + " }", + " else {", + " if (this.startRotation.type === 'rotation') {", + " sprite.rotation = 0;", + " }", + " else {", + " sprite.rotation = this.startRotation.genValue(emissionState.time / this.duration);", + " }", + " }", + " }", + " else if (this.rendererSettings.renderMode === RenderMode.Trail) {", + " const trail = particle;", + " trail.length = this.rendererEmitterSettings.startLength.genValue(emissionState.time / this.duration);", + " }", + " this.emitterShape.initialize(particle);", + " if (this.rendererSettings.renderMode === RenderMode.Trail &&", + " this.rendererEmitterSettings.followLocalOrigin) {", + " const trail = particle;", + " trail.localPosition = new THREE.Vector3().copy(trail.position);", + " }", + " if (this.worldSpace) {", + " particle.position.applyMatrix4(matrix);", + " particle.startSize =", + " (particle.startSize * (Math.abs(scale.x) + Math.abs(scale.y) + Math.abs(scale.z))) / 3;", + " particle.size = particle.startSize;", + " particle.velocity.multiply(scale).applyMatrix3(this.normalMatrix);", + " if (particle.rotation && particle.rotation instanceof THREE.Quaternion) {", + " particle.rotation.multiplyQuaternions(tempQ$1, particle.rotation);", + " }", + " }", + " else {", + " if (this.onlyUsedByOther) {", + " particle.parentMatrix = matrix;", + " }", + " }", + " for (let j = 0; j < this.behaviors.length; j++) {", + " this.behaviors[j].initialize(particle);", + " }", + " }", + " }", + " endEmit() {", + " this.emitEnded = true;", + " if (this.autoDestroy) {", + " this.markForDestroy = true;", + " }", + " }", + " dispose() {", + " if (this._renderer)", + " this._renderer.deleteSystem(this);", + " this.emitter.dispose();", + " if (this.emitter.parent)", + " this.emitter.parent.remove(this.emitter);", + " }", + " restart() {", + " this.paused = false;", + " this.particleNum = 0;", + " this.emissionState.burstIndex = 0;", + " this.emissionState.burstWaveIndex = 0;", + " this.emissionState.time = 0;", + " this.emissionState.waitEmiting = 0;", + " this.behaviors.forEach((behavior) => {", + " behavior.reset();", + " });", + " this.emitEnded = false;", + " this.markForDestroy = false;", + " this.prewarmed = false;", + " }", + " update(delta) {", + " if (this.paused)", + " return;", + " let currentParent = this.emitter;", + " while (currentParent.parent) {", + " currentParent = currentParent.parent;", + " }", + " if (currentParent.type !== 'Scene') {", + " this.dispose();", + " return;", + " }", + " if (this.firstTimeUpdate) {", + " this.firstTimeUpdate = false;", + " this.emitter.updateWorldMatrix(true, false);", + " }", + " if (this.emitEnded && this.particleNum === 0) {", + " if (this.markForDestroy && this.emitter.parent)", + " this.dispose();", + " return;", + " }", + " if (this.looping && this.prewarm && !this.prewarmed) {", + " this.prewarmed = true;", + " for (let i = 0; i < this.duration * PREWARM_FPS$1; i++) {", + " this.update(1.0 / PREWARM_FPS$1);", + " }", + " }", + " if (delta > 0.1) {", + " delta = 0.1;", + " }", + " if (this.neededToUpdateRender) {", + " if (this._renderer)", + " this._renderer.updateSystem(this);", + " this.neededToUpdateRender = false;", + " }", + " if (!this.onlyUsedByOther) {", + " this.emit(delta, this.emissionState, this.emitter.matrixWorld);", + " }", + " this.emitterShape.update(this, delta);", + " for (let j = 0; j < this.behaviors.length; j++) {", + " for (let i = 0; i < this.particleNum; i++) {", + " if (!this.particles[i].died) {", + " this.behaviors[j].update(this.particles[i], delta);", + " }", + " }", + " this.behaviors[j].frameUpdate(delta);", + " }", + " for (let i = 0; i < this.particleNum; i++) {", + " if (this.rendererEmitterSettings.followLocalOrigin &&", + " this.particles[i].localPosition) {", + " this.particles[i].position.copy(this.particles[i].localPosition);", + " if (this.particles[i].parentMatrix) {", + " this.particles[i].position.applyMatrix4(this.particles[i].parentMatrix);", + " }", + " else {", + " this.particles[i].position.applyMatrix4(this.emitter.matrixWorld);", + " }", + " }", + " else {", + " this.particles[i].position.addScaledVector(this.particles[i].velocity, delta * this.particles[i].speedModifier);", + " }", + " this.particles[i].age += delta;", + " }", + " if (this.rendererSettings.renderMode === RenderMode.Trail) {", + " for (let i = 0; i < this.particleNum; i++) {", + " const particle = this.particles[i];", + " particle.update();", + " }", + " }", + " for (let i = 0; i < this.particleNum; i++) {", + " const particle = this.particles[i];", + " if (particle.died && (!(particle instanceof TrailParticle) || particle.previous.length === 0)) {", + " this.particles[i] = this.particles[this.particleNum - 1];", + " this.particles[this.particleNum - 1] = particle;", + " this.particleNum--;", + " i--;", + " }", + " }", + " }", + " emit(delta, emissionState, emitterMatrix) {", + " if (emissionState.time > this.duration) {", + " if (this.looping) {", + " emissionState.time -= this.duration;", + " emissionState.burstIndex = 0;", + " this.behaviors.forEach((behavior) => {", + " behavior.reset();", + " });", + " }", + " else {", + " if (!this.emitEnded && !this.onlyUsedByOther) {", + " this.endEmit();", + " }", + " }", + " }", + " this.normalMatrix.getNormalMatrix(emitterMatrix);", + " const totalSpawn = Math.ceil(emissionState.waitEmiting);", + " this.spawn(totalSpawn, emissionState, emitterMatrix);", + " emissionState.waitEmiting -= totalSpawn;", + " while (emissionState.burstIndex < this.emissionBursts.length &&", + " this.emissionBursts[emissionState.burstIndex].time <= emissionState.time) {", + " if (Math.random() < this.emissionBursts[emissionState.burstIndex].probability) {", + " const count = this.emissionBursts[emissionState.burstIndex].count.genValue(this.time);", + " this.spawn(count, emissionState, emitterMatrix);", + " }", + " emissionState.burstIndex++;", + " }", + " if (!this.emitEnded) {", + " emissionState.waitEmiting += delta * this.emissionOverTime.genValue(emissionState.time / this.duration);", + " if (emissionState.previousWorldPos != undefined) {", + " this.temp.set(emitterMatrix.elements[12], emitterMatrix.elements[13], emitterMatrix.elements[14]);", + " emissionState.travelDistance += emissionState.previousWorldPos.distanceTo(this.temp);", + " const emitPerMeter = this.emissionOverDistance.genValue(emissionState.time / this.duration);", + " if (emissionState.travelDistance * emitPerMeter > 0) {", + " const count = Math.floor(emissionState.travelDistance * emitPerMeter);", + " emissionState.travelDistance -= count / emitPerMeter;", + " emissionState.waitEmiting += count;", + " }", + " }", + " }", + " if (emissionState.previousWorldPos === undefined)", + " emissionState.previousWorldPos = new THREE.Vector3();", + " emissionState.previousWorldPos.set(emitterMatrix.elements[12], emitterMatrix.elements[13], emitterMatrix.elements[14]);", + " emissionState.time += delta;", + " }", + " toJSON(meta, options = {}) {", + " const isRootObject = meta === undefined || typeof meta === 'string';", + " if (isRootObject) {", + " meta = {", + " geometries: {},", + " materials: {},", + " textures: {},", + " images: {},", + " shapes: {},", + " skeletons: {},", + " animations: {},", + " nodes: {},", + " };", + " }", + " meta.materials[this.rendererSettings.material.uuid] = this.rendererSettings.material.toJSON(meta);", + " if (options.useUrlForImage) {", + " if (this.texture.source !== undefined) {", + " const image = this.texture.source;", + " meta.images[image.uuid] = {", + " uuid: image.uuid,", + " url: this.texture.image.url,", + " };", + " }", + " }", + " let rendererSettingsJSON;", + " if (this.renderMode === RenderMode.Trail) {", + " rendererSettingsJSON = {", + " startLength: this.rendererEmitterSettings.startLength.toJSON(),", + " followLocalOrigin: this.rendererEmitterSettings.followLocalOrigin,", + " };", + " }", + " else if (this.renderMode === RenderMode.Mesh) {", + " rendererSettingsJSON = {};", + " }", + " else if (this.renderMode === RenderMode.StretchedBillBoard) {", + " rendererSettingsJSON = {", + " speedFactor: this.rendererEmitterSettings.speedFactor,", + " lengthFactor: this.rendererEmitterSettings.lengthFactor,", + " };", + " }", + " else {", + " rendererSettingsJSON = {};", + " }", + " const geometry = this.rendererSettings.instancingGeometry;", + " if (meta.geometries && !meta.geometries[geometry.uuid]) {", + " meta.geometries[geometry.uuid] = geometry.toJSON();", + " }", + " return {", + " version: '2.0',", + " autoDestroy: this.autoDestroy,", + " looping: this.looping,", + " prewarm: this.prewarm,", + " duration: this.duration,", + " shape: this.emitterShape.toJSON(),", + " startLife: this.startLife.toJSON(),", + " startSpeed: this.startSpeed.toJSON(),", + " startRotation: this.startRotation.toJSON(),", + " startSize: this.startSize.toJSON(),", + " startColor: this.startColor.toJSON(),", + " emissionOverTime: this.emissionOverTime.toJSON(),", + " emissionOverDistance: this.emissionOverDistance.toJSON(),", + " emissionBursts: this.emissionBursts.map((burst) => ({", + " time: burst.time,", + " count: burst.count.toJSON(),", + " probability: burst.probability,", + " interval: burst.interval,", + " cycle: burst.cycle,", + " })),", + " onlyUsedByOther: this.onlyUsedByOther,", + " instancingGeometry: this.rendererSettings.instancingGeometry.uuid,", + " renderOrder: this.renderOrder,", + " renderMode: this.renderMode,", + " rendererEmitterSettings: rendererSettingsJSON,", + " material: this.rendererSettings.material.uuid,", + " layers: this.layers.mask,", + " startTileIndex: this.startTileIndex.toJSON(),", + " uTileCount: this.uTileCount,", + " vTileCount: this.vTileCount,", + " behaviors: this.behaviors.map((behavior) => behavior.toJSON()),", + " worldSpace: this.worldSpace,", + " };", + " }", + " static fromJSON(json, meta, dependencies) {", + " var _a, _b;", + " const shape = EmitterFromJSON(json.shape, meta);", + " let rendererEmitterSettings;", + " if (json.renderMode === RenderMode.Trail) {", + " let trailSettings = json.rendererEmitterSettings;", + " rendererEmitterSettings = {", + " startLength: trailSettings.startLength != undefined", + " ? ValueGeneratorFromJSON(trailSettings.startLength)", + " : new ConstantValue(30),", + " followLocalOrigin: trailSettings.followLocalOrigin,", + " };", + " }", + " else if (json.renderMode === RenderMode.Mesh) {", + " rendererEmitterSettings = {};", + " }", + " else if (json.renderMode === RenderMode.StretchedBillBoard) {", + " rendererEmitterSettings = json.rendererEmitterSettings;", + " if (json.speedFactor != undefined) {", + " rendererEmitterSettings.speedFactor = json.speedFactor;", + " }", + " }", + " else {", + " rendererEmitterSettings = {};", + " }", + " const layers = new THREE.Layers();", + " if (json.layers) {", + " layers.mask = json.layers;", + " }", + " const ps = new ParticleSystem({", + " autoDestroy: json.autoDestroy,", + " looping: json.looping,", + " prewarm: json.prewarm,", + " duration: json.duration,", + " shape: shape,", + " startLife: ValueGeneratorFromJSON(json.startLife),", + " startSpeed: ValueGeneratorFromJSON(json.startSpeed),", + " startRotation: GeneratorFromJSON(json.startRotation),", + " startSize: ValueGeneratorFromJSON(json.startSize),", + " startColor: ColorGeneratorFromJSON(json.startColor),", + " emissionOverTime: ValueGeneratorFromJSON(json.emissionOverTime),", + " emissionOverDistance: ValueGeneratorFromJSON(json.emissionOverDistance),", + " emissionBursts: (_a = json.emissionBursts) === null || _a === void 0 ? void 0 : _a.map((burst) => ({", + " time: burst.time,", + " count: typeof burst.count === 'number'", + " ? new ConstantValue(burst.count)", + " : ValueGeneratorFromJSON(burst.count),", + " probability: burst.probability,", + " interval: burst.interval,", + " cycle: burst.cycle,", + " })),", + " onlyUsedByOther: json.onlyUsedByOther,", + " instancingGeometry: meta.geometries[json.instancingGeometry],", + " renderMode: json.renderMode,", + " rendererEmitterSettings: rendererEmitterSettings,", + " renderOrder: json.renderOrder,", + " layers: layers,", + " material: json.material", + " ? meta.materials[json.material]", + " : json.texture", + " ? new THREE.MeshBasicMaterial({", + " map: meta.textures[json.texture],", + " transparent: (_b = json.transparent) !== null && _b !== void 0 ? _b : true,", + " blending: json.blending,", + " side: THREE.DoubleSide,", + " })", + " : new THREE.MeshBasicMaterial({", + " color: 0xffffff,", + " transparent: true,", + " blending: THREE.AdditiveBlending,", + " side: THREE.DoubleSide,", + " }),", + " startTileIndex: typeof json.startTileIndex === 'number'", + " ? new ConstantValue(json.startTileIndex)", + " : ValueGeneratorFromJSON(json.startTileIndex),", + " uTileCount: json.uTileCount,", + " vTileCount: json.vTileCount,", + " behaviors: [],", + " worldSpace: json.worldSpace,", + " });", + " ps.behaviors = json.behaviors.map((behaviorJson) => {", + " const behavior = BehaviorFromJSON(behaviorJson, ps);", + " if (behavior.type === 'EmitSubParticleSystem') {", + " dependencies[behaviorJson.subParticleSystem] = behavior;", + " }", + " return behavior;", + " });", + " return ps;", + " }", + " addBehavior(behavior) {", + " this.behaviors.push(behavior);", + " }", + " getRendererSettings() {", + " return this.rendererSettings;", + " }", + " clone() {", + " const newEmissionBursts = [];", + " for (const emissionBurst of this.emissionBursts) {", + " const newEmissionBurst = {};", + " Object.assign(newEmissionBurst, emissionBurst);", + " newEmissionBursts.push(newEmissionBurst);", + " }", + " const newBehaviors = [];", + " for (const behavior of this.behaviors) {", + " newBehaviors.push(behavior.clone());", + " }", + " let rendererEmitterSettings;", + " if (this.renderMode === RenderMode.Trail) {", + " rendererEmitterSettings = {", + " startLength: this.rendererEmitterSettings.startLength.clone(),", + " followLocalOrigin: this.rendererEmitterSettings.followLocalOrigin,", + " };", + " }", + " else {", + " rendererEmitterSettings = {};", + " }", + " const layers = new THREE.Layers();", + " layers.mask = this.layers.mask;", + " return new ParticleSystem({", + " autoDestroy: this.autoDestroy,", + " looping: this.looping,", + " duration: this.duration,", + " shape: this.emitterShape.clone(),", + " startLife: this.startLife.clone(),", + " startSpeed: this.startSpeed.clone(),", + " startRotation: this.startRotation.clone(),", + " startSize: this.startSize.clone(),", + " startColor: this.startColor.clone(),", + " emissionOverTime: this.emissionOverTime.clone(),", + " emissionOverDistance: this.emissionOverDistance.clone(),", + " emissionBursts: newEmissionBursts,", + " onlyUsedByOther: this.onlyUsedByOther,", + " instancingGeometry: this.rendererSettings.instancingGeometry,", + " renderMode: this.renderMode,", + " renderOrder: this.renderOrder,", + " rendererEmitterSettings: rendererEmitterSettings,", + " material: this.rendererSettings.material,", + " startTileIndex: this.startTileIndex,", + " uTileCount: this.uTileCount,", + " vTileCount: this.vTileCount,", + " behaviors: newBehaviors,", + " worldSpace: this.worldSpace,", + " layers: layers,", + " });", + " }", + "}", + "", + "var particle_frag = `", + "", + "#include ", + "#include ", + "#include ", + "#include ", + "#include ", + "#include ", + "#include ", + "", + "void main() {", + "", + " #include ", + " ", + " vec3 outgoingLight = vec3( 0.0 );", + " vec4 diffuseColor = vColor;", + " ", + " #include ", + " ", + " #ifdef USE_MAP", + " diffuseColor *= texture2D( map, vMapUv);", + " #endif", + " ", + " #include ", + "", + " outgoingLight = diffuseColor.rgb;", + " ", + " #ifdef USE_COLOR_AS_ALPHA", + " gl_FragColor = vec4( outgoingLight, diffuseColor.r );", + " #else", + " gl_FragColor = vec4( outgoingLight, diffuseColor.a );", + " #endif", + " ", + " ", + " #include ", + "", + "}", + "`;", + "", + "var particle_physics_frag = `", + "#define STANDARD", + "#ifdef PHYSICAL", + "#define IOR", + "#define SPECULAR", + "#endif", + "uniform vec3 diffuse;", + "uniform vec3 emissive;", + "uniform float roughness;", + "uniform float metalness;", + "uniform float opacity;", + "#ifdef IOR", + "uniform float ior;", + "#endif", + "#ifdef SPECULAR", + "uniform float specularIntensity;", + "uniform vec3 specularColor;", + "#ifdef USE_SPECULARINTENSITYMAP", + "uniform sampler2D specularIntensityMap;", + "#endif", + "#ifdef USE_SPECULARCOLORMAP", + "uniform sampler2D specularColorMap;", + "#endif", + "#endif", + "#ifdef USE_CLEARCOAT", + "uniform float clearcoat;", + "uniform float clearcoatRoughness;", + "#endif", + "#ifdef USE_IRIDESCENCE", + "uniform float iridescence;", + "uniform float iridescenceIOR;", + "uniform float iridescenceThicknessMinimum;", + "uniform float iridescenceThicknessMaximum;", + "#endif", + "#ifdef USE_SHEEN", + "uniform vec3 sheenColor;", + "uniform float sheenRoughness;", + "#ifdef USE_SHEENCOLORMAP", + "uniform sampler2D sheenColorMap;", + "#endif", + "#ifdef USE_SHEENROUGHNESSMAP", + "uniform sampler2D sheenRoughnessMap;", + "#endif", + "#endif", + "", + "varying vec3 vViewPosition;", + "#include ", + "#include ", + "#include ", + "#include ", + "#include ", + "#include ", + "#include ", + "#include ", + "#include ", + "#include ", + "#include ", + "#include ", + "#include ", + "#include ", + "#include ", + "#include ", + "#include ", + "#include ", + "#include ", + "#include ", + "#include ", + "#include ", + "#include ", + "#include ", + "#include ", + "#include ", + "#include ", + "#include ", + "#include ", + "#include ", + "", + "void main() {", + " #include ", + " vec4 diffuseColor = vec4( diffuse, opacity );", + " ReflectedLight reflectedLight = ReflectedLight( vec3( 0.0 ), vec3( 0.0 ), vec3( 0.0 ), vec3( 0.0 ) );", + " vec3 totalEmissiveRadiance = emissive;", + " #include ", + " #include ", + " #include ", + " #include ", + " #include ", + " #include ", + " #include ", + " #include ", + " #include ", + " #include ", + " #include ", + " #include ", + " // accumulation", + " #include ", + " #include ", + " #include ", + " #include ", + " // modulation", + " #include ", + " vec3 totalDiffuse = reflectedLight.directDiffuse + reflectedLight.indirectDiffuse;", + " vec3 totalSpecular = reflectedLight.directSpecular + reflectedLight.indirectSpecular;", + " #include ", + " vec3 outgoingLight = totalDiffuse + totalSpecular + totalEmissiveRadiance;", + " #ifdef USE_SHEEN", + " // Sheen energy compensation approximation calculation can be found at the end of", + " // https://drive.google.com/file/d/1T0D1VSyR4AllqIJTQAraEIzjlb5h4FKH/view?usp=sharing", + " float sheenEnergyComp = 1.0 - 0.157 * max3( material.sheenColor );", + " outgoingLight = outgoingLight * sheenEnergyComp + sheenSpecular;", + " #endif", + " #ifdef USE_CLEARCOAT", + " float dotNVcc = saturate( dot( geometry.clearcoatNormal, geometry.viewDir ) );", + " vec3 Fcc = F_Schlick( material.clearcoatF0, material.clearcoatF90, dotNVcc );", + " outgoingLight = outgoingLight * ( 1.0 - material.clearcoat * Fcc ) + clearcoatSpecular * material.clearcoat;", + " #endif", + " #include ", + " #include ", + " #include ", + " #include ", + " #include ", + " #include ", + "}`;", + "", + "var uv_vertex_tile = `", + "", + " #ifdef UV_TILE", + " float col = mod(uvTile, tileCount.x);", + " float row = (tileCount.y - floor(uvTile / tileCount.x) - 1.0);", + " ", + " mat3 tileTransform = mat3(", + " 1.0 / tileCount.x, 0.0, 0.0,", + " 0.0, 1.0 / tileCount.y, 0.0, ", + " col / tileCount.x, row / tileCount.y, 1.0);", + " #else", + " mat3 tileTransform = mat3(1.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 1.0);", + " #endif", + "", + "#if defined( USE_UV ) || defined( USE_ANISOTROPY )", + "", + "vUv = (tileTransform *vec3( uv, 1 )).xy;", + "", + "#endif", + "#ifdef USE_MAP", + "", + "vMapUv = ( tileTransform * (mapTransform * vec3( MAP_UV, 1 ) )).xy;", + "", + "#endif", + "#ifdef USE_ALPHAMAP", + "", + "vAlphaMapUv = ( tileTransform * (alphaMapTransform * vec3( ALPHAMAP_UV, 1 ) )).xy;", + "", + "#endif", + "#ifdef USE_LIGHTMAP", + "", + "vLightMapUv = ( tileTransform * (lightMapTransform * vec3( LIGHTMAP_UV, 1 ) )).xy;", + "", + "#endif", + "#ifdef USE_AOMAP", + "", + "vAoMapUv = ( tileTransform * (aoMapTransform * vec3( AOMAP_UV, 1 ) )).xy;", + "", + "#endif", + "#ifdef USE_BUMPMAP", + "", + "vBumpMapUv = ( tileTransform * (bumpMapTransform * vec3( BUMPMAP_UV, 1 ) )).xy;", + "", + "#endif", + "#ifdef USE_NORMALMAP", + "", + "vNormalMapUv = ( tileTransform * (normalMapTransform * vec3( NORMALMAP_UV, 1 ) )).xy;", + "", + "#endif", + "#ifdef USE_DISPLACEMENTMAP", + "", + "vDisplacementMapUv = ( tileTransform * (displacementMapTransform * vec3( DISPLACEMENTMAP_UV, 1 ) )).xy;", + "", + "#endif", + "#ifdef USE_EMISSIVEMAP", + "", + "vEmissiveMapUv = ( tileTransform * (emissiveMapTransform * vec3( EMISSIVEMAP_UV, 1 ) )).xy;", + "", + "#endif", + "#ifdef USE_METALNESSMAP", + "", + "vMetalnessMapUv = ( tileTransform * (metalnessMapTransform * vec3( METALNESSMAP_UV, 1 ) )).xy;", + "", + "#endif", + "#ifdef USE_ROUGHNESSMAP", + "", + "vRoughnessMapUv = ( tileTransform * (roughnessMapTransform * vec3( ROUGHNESSMAP_UV, 1 ) )).xy;", + "", + "#endif", + "#ifdef USE_ANISOTROPYMAP", + "", + "vAnisotropyMapUv = ( tileTransform * (anisotropyMapTransform * vec3( ANISOTROPYMAP_UV, 1 ) )).xy;", + "", + "#endif", + "#ifdef USE_CLEARCOATMAP", + "", + "vClearcoatMapUv = ( tileTransform * (clearcoatMapTransform * vec3( CLEARCOATMAP_UV, 1 ) )).xy;", + "", + "#endif", + "#ifdef USE_CLEARCOAT_NORMALMAP", + "", + "vClearcoatNormalMapUv = ( tileTransform * (clearcoatNormalMapTransform * vec3( CLEARCOAT_NORMALMAP_UV, 1 ) )).xy;", + "", + "#endif", + "#ifdef USE_CLEARCOAT_ROUGHNESSMAP", + "", + "vClearcoatRoughnessMapUv = ( tileTransform * (clearcoatRoughnessMapTransform * vec3( CLEARCOAT_ROUGHNESSMAP_UV, 1 ) )).xy;", + "", + "#endif", + "#ifdef USE_IRIDESCENCEMAP", + "", + "vIridescenceMapUv = ( tileTransform * (iridescenceMapTransform * vec3( IRIDESCENCEMAP_UV, 1 ) )).xy;", + "", + "#endif", + "#ifdef USE_IRIDESCENCE_THICKNESSMAP", + "", + "vIridescenceThicknessMapUv = ( tileTransform * (iridescenceThicknessMapTransform * vec3( IRIDESCENCE_THICKNESSMAP_UV, 1 ) )).xy;", + "", + "#endif", + "#ifdef USE_SHEEN_COLORMAP", + "", + "vSheenColorMapUv = ( tileTransform * (sheenColorMapTransform * vec3( SHEEN_COLORMAP_UV, 1 ) )).xy;", + "", + "#endif", + "#ifdef USE_SHEEN_ROUGHNESSMAP", + "", + "vSheenRoughnessMapUv = ( tileTransform * (sheenRoughnessMapTransform * vec3( SHEEN_ROUGHNESSMAP_UV, 1 ) )).xy;", + "", + "#endif", + "#ifdef USE_SPECULARMAP", + "", + "vSpecularMapUv = ( tileTransform * (specularMapTransform * vec3( SPECULARMAP_UV, 1 ) )).xy;", + "", + "#endif", + "#ifdef USE_SPECULAR_COLORMAP", + "", + "vSpecularColorMapUv = ( tileTransform * (specularColorMapTransform * vec3( SPECULAR_COLORMAP_UV, 1 ) )).xy;", + "", + "#endif", + "#ifdef USE_SPECULAR_INTENSITYMAP", + "", + "vSpecularIntensityMapUv = ( tileTransform * (specularIntensityMapTransform * vec3( SPECULAR_INTENSITYMAP_UV, 1 ) )).xy;", + "", + "#endif", + "#ifdef USE_TRANSMISSIONMAP", + "", + "vTransmissionMapUv = ( tileTransform * transmissionMapTransform * vec3( TRANSMISSIONMAP_UV, 1 ) )).xy;", + "", + "#endif", + "#ifdef USE_THICKNESSMAP", + "", + "vThicknessMapUv = ( tileTransform * thicknessMapTransform * vec3( THICKNESSMAP_UV, 1 ) )).xy;", + "", + "#endif", + "`;", + "", + "var particle_vert = `", + "#include ", + "#include ", + "#include ", + "#include ", + "#include ", + "", + "attribute vec3 offset;", + "attribute float rotation;", + "attribute float size;", + "attribute float uvTile;", + "", + "#ifdef UV_TILE", + "uniform vec2 tileCount;", + "#endif", + "", + "void main() {", + "", + " ${uv_vertex_tile}", + "\t", + " vec2 alignedPosition = ( position.xy ) * size;", + " ", + " vec2 rotatedPosition;", + " rotatedPosition.x = cos( rotation ) * alignedPosition.x - sin( rotation ) * alignedPosition.y;", + " rotatedPosition.y = sin( rotation ) * alignedPosition.x + cos( rotation ) * alignedPosition.y;", + "#ifdef HORIZONTAL", + " vec4 mvPosition = modelMatrix * vec4( offset, 1.0 );", + " mvPosition.x += rotatedPosition.x;", + " mvPosition.z -= rotatedPosition.y;", + " mvPosition = viewMatrix * mvPosition;", + "#elif defined(VERTICAL)", + " vec4 mvPosition = modelMatrix * vec4( offset, 1.0 );", + " mvPosition.y += rotatedPosition.y;", + " mvPosition = viewMatrix * mvPosition;", + " mvPosition.x += rotatedPosition.x;", + "#else", + " vec4 mvPosition = modelViewMatrix * vec4( offset, 1.0 );", + " mvPosition.xy += rotatedPosition;", + "#endif", + "", + "\tvColor = color;", + "", + "\tgl_Position = projectionMatrix * mvPosition;", + "", + "\t#include ", + "\t#include ", + "", + "}", + "`;", + "", + "var local_particle_vert = `", + "#include ", + "#include ", + "#include ", + "#include ", + "#include ", + "", + "attribute vec3 offset;", + "attribute vec4 rotation;", + "attribute float size;", + "// attribute vec4 color;", + "attribute float uvTile;", + "", + "#ifdef UV_TILE", + "uniform vec2 tileCount;", + "#endif", + "", + "void main() {", + "", + " ${uv_vertex_tile}", + " ", + " float x2 = rotation.x + rotation.x, y2 = rotation.y + rotation.y, z2 = rotation.z + rotation.z;", + " float xx = rotation.x * x2, xy = rotation.x * y2, xz = rotation.x * z2;", + " float yy = rotation.y * y2, yz = rotation.y * z2, zz = rotation.z * z2;", + " float wx = rotation.w * x2, wy = rotation.w * y2, wz = rotation.w * z2;", + " float sx = size, sy = size, sz = size;", + " ", + " mat4 matrix = mat4(( 1.0 - ( yy + zz ) ) * sx, ( xy + wz ) * sx, ( xz - wy ) * sx, 0.0, // 1. column", + " ( xy - wz ) * sy, ( 1.0 - ( xx + zz ) ) * sy, ( yz + wx ) * sy, 0.0, // 2. column", + " ( xz + wy ) * sz, ( yz - wx ) * sz, ( 1.0 - ( xx + yy ) ) * sz, 0.0, // 3. column", + " offset.x, offset.y, offset.z, 1.0);", + " ", + " vec4 mvPosition = modelViewMatrix * (matrix * vec4( position, 1.0 ));", + "", + "\tvColor = color;", + "", + "\tgl_Position = projectionMatrix * mvPosition;", + "", + "\t#include ", + "\t#include ", + "", + "}", + "`;", + "", + "var local_particle_physics_vert = `", + "#define STANDARD", + "varying vec3 vViewPosition;", + "#ifdef USE_TRANSMISSION", + "\tvarying vec3 vWorldPosition;", + "#endif", + "#include ", + "#include ", + "", + "attribute vec3 offset;", + "attribute vec4 rotation;", + "attribute float size;", + "attribute float uvTile;", + "", + "#ifdef UV_TILE", + "uniform vec2 tileCount;", + "#endif", + "", + "#include ", + "#include ", + "#include ", + "#include ", + "#include ", + "#include ", + "#include ", + "#include ", + "#include ", + "", + "void main() {", + " ${uv_vertex_tile}", + "", + " float x2 = rotation.x + rotation.x, y2 = rotation.y + rotation.y, z2 = rotation.z + rotation.z;", + " float xx = rotation.x * x2, xy = rotation.x * y2, xz = rotation.x * z2;", + " float yy = rotation.y * y2, yz = rotation.y * z2, zz = rotation.z * z2;", + " float wx = rotation.w * x2, wy = rotation.w * y2, wz = rotation.w * z2;", + " float sx = size, sy = size, sz = size;", + "", + " mat4 particleMatrix = mat4(( 1.0 - ( yy + zz ) ) * sx, ( xy + wz ) * sx, ( xz - wy ) * sx, 0.0, // 1. column", + " ( xy - wz ) * sy, ( 1.0 - ( xx + zz ) ) * sy, ( yz + wx ) * sy, 0.0, // 2. column", + " ( xz + wy ) * sz, ( yz - wx ) * sz, ( 1.0 - ( xx + yy ) ) * sz, 0.0, // 3. column", + " offset.x, offset.y, offset.z, 1.0);", + "", + "\t#include ", + "\t#include ", + "\t#include ", + "\t#include ", + "\t#include ", + "\t#include ", + "", + "\t// replace defaultnormal_vertex", + "\tvec3 transformedNormal = objectNormal;", + " mat3 m = mat3( particleMatrix );", + " transformedNormal /= vec3( dot( m[ 0 ], m[ 0 ] ), dot( m[ 1 ], m[ 1 ] ), dot( m[ 2 ], m[ 2 ] ) );", + " transformedNormal = m * transformedNormal;", + " transformedNormal = normalMatrix * transformedNormal;", + " #ifdef FLIP_SIDED", + " transformedNormal = - transformedNormal;", + " #endif", + " #ifdef USE_TANGENT", + " vec3 transformedTangent = ( modelViewMatrix * vec4( objectTangent, 0.0 ) ).xyz;", + " #ifdef FLIP_SIDED", + " transformedTangent = - transformedTangent;", + " #endif", + " #endif", + "", + "\t#include ", + "\t#include ", + "\t#include ", + "\t#include ", + "\t#include ", + "", + "\t// replace include ", + " vec4 mvPosition = vec4( transformed, 1.0 );", + " mvPosition = modelViewMatrix * (particleMatrix * mvPosition);", + "\tgl_Position = projectionMatrix * mvPosition;", + "", + "\t#include ", + "\t#include ", + "\t", + "\tvViewPosition = - mvPosition.xyz;", + "\t", + "\t#include ", + "\t#include ", + "\t#include ", + "#ifdef USE_TRANSMISSION", + "\tvWorldPosition = worldPosition.xyz;", + "#endif", + "}", + "`;", + "", + "var stretched_bb_particle_vert = `", + "#include ", + "#include ", + "#include ", + "#include ", + "#include ", + "", + "attribute vec3 offset;", + "attribute float rotation;", + "attribute float size;", + "attribute vec4 velocity;", + "attribute float uvTile;", + "", + "#ifdef UV_TILE", + "uniform vec2 tileCount;", + "#endif", + "", + "uniform float speedFactor;", + "", + "void main() {", + "", + " ${uv_vertex_tile}", + " ", + " float lengthFactor = velocity.w;", + "#ifdef USE_SKEW", + " vec4 mvPosition = modelViewMatrix * vec4( offset, 1.0 );", + " vec3 viewVelocity = normalMatrix * velocity.xyz;", + "", + " vec3 scaledPos = vec3(position.xy * size, position.z);", + " float vlength = length(viewVelocity);", + " vec3 projVelocity = dot(scaledPos, viewVelocity) * viewVelocity / vlength;", + " mvPosition.xyz += scaledPos + projVelocity * (speedFactor / size + lengthFactor / vlength);", + "#else", + " vec4 mvPosition = modelViewMatrix * vec4( offset, 1.0 );", + " vec3 viewVelocity = normalMatrix * velocity.xyz;", + " float vlength = length(viewVelocity); ", + " mvPosition.xyz += position.y * normalize(cross(mvPosition.xyz, viewVelocity)) * size; // switch the cross to match unity implementation", + " mvPosition.xyz -= (position.x + 0.5) * viewVelocity * (1.0 + lengthFactor / vlength) * size; // minus position.x to match unity implementation", + "#endif", + "\tvColor = color;", + "\tgl_Position = projectionMatrix * mvPosition;", + "\t#include ", + "\t#include ", + "}", + "`;", + "", + "function getMaterialUVChannelName(value) {", + " if (value === 0)", + " return 'uv';", + " return `uv${value}`;", + "}", + "", + "new THREE.Vector3(0, 0, 1);", + "class SpriteBatch extends VFXBatch {", + " constructor(settings) {", + " super(settings);", + " this.vector_ = new THREE.Vector3();", + " this.vector2_ = new THREE.Vector3();", + " this.vector3_ = new THREE.Vector3();", + " this.quaternion_ = new THREE.Quaternion();", + " this.quaternion2_ = new THREE.Quaternion();", + " this.quaternion3_ = new THREE.Quaternion();", + " this.rotationMat_ = new THREE.Matrix3();", + " this.rotationMat2_ = new THREE.Matrix3();", + " this.maxParticles = 1000;", + " this.setupBuffers();", + " this.rebuildMaterial();", + " }", + " buildExpandableBuffers() {", + " this.offsetBuffer = new THREE.InstancedBufferAttribute(new Float32Array(this.maxParticles * 3), 3);", + " this.offsetBuffer.setUsage(THREE.DynamicDrawUsage);", + " this.geometry.setAttribute('offset', this.offsetBuffer);", + " this.colorBuffer = new THREE.InstancedBufferAttribute(new Float32Array(this.maxParticles * 4), 4);", + " this.colorBuffer.setUsage(THREE.DynamicDrawUsage);", + " this.geometry.setAttribute('color', this.colorBuffer);", + " if (this.settings.renderMode === RenderMode.Mesh) {", + " this.rotationBuffer = new THREE.InstancedBufferAttribute(new Float32Array(this.maxParticles * 4), 4);", + " this.rotationBuffer.setUsage(THREE.DynamicDrawUsage);", + " this.geometry.setAttribute('rotation', this.rotationBuffer);", + " }", + " else if (this.settings.renderMode === RenderMode.BillBoard ||", + " this.settings.renderMode === RenderMode.HorizontalBillBoard ||", + " this.settings.renderMode === RenderMode.VerticalBillBoard ||", + " this.settings.renderMode === RenderMode.StretchedBillBoard) {", + " this.rotationBuffer = new THREE.InstancedBufferAttribute(new Float32Array(this.maxParticles), 1);", + " this.rotationBuffer.setUsage(THREE.DynamicDrawUsage);", + " this.geometry.setAttribute('rotation', this.rotationBuffer);", + " }", + " this.sizeBuffer = new THREE.InstancedBufferAttribute(new Float32Array(this.maxParticles), 1);", + " this.sizeBuffer.setUsage(THREE.DynamicDrawUsage);", + " this.geometry.setAttribute('size', this.sizeBuffer);", + " this.uvTileBuffer = new THREE.InstancedBufferAttribute(new Float32Array(this.maxParticles), 1);", + " this.uvTileBuffer.setUsage(THREE.DynamicDrawUsage);", + " this.geometry.setAttribute('uvTile', this.uvTileBuffer);", + " if (this.settings.renderMode === RenderMode.StretchedBillBoard) {", + " this.velocityBuffer = new THREE.InstancedBufferAttribute(new Float32Array(this.maxParticles * 4), 4);", + " this.velocityBuffer.setUsage(THREE.DynamicDrawUsage);", + " this.geometry.setAttribute('velocity', this.velocityBuffer);", + " }", + " }", + " setupBuffers() {", + " if (this.geometry)", + " this.geometry.dispose();", + " this.geometry = new THREE.InstancedBufferGeometry();", + " this.geometry.setIndex(this.settings.instancingGeometry.getIndex());", + " if (this.settings.instancingGeometry.hasAttribute('normal')) {", + " this.geometry.setAttribute('normal', this.settings.instancingGeometry.getAttribute('normal'));", + " }", + " this.geometry.setAttribute('position', this.settings.instancingGeometry.getAttribute('position'));", + " this.geometry.setAttribute('uv', this.settings.instancingGeometry.getAttribute('uv'));", + " this.buildExpandableBuffers();", + " }", + " expandBuffers(target) {", + " while (target >= this.maxParticles) {", + " this.maxParticles *= 2;", + " }", + " this.setupBuffers();", + " }", + " rebuildMaterial() {", + " this.layers.mask = this.settings.layers.mask;", + " let uniforms;", + " const defines = {};", + " if (this.settings.material.type === 'MeshStandardMaterial' ||", + " this.settings.material.type === 'MeshPhysicalMaterial') {", + " const mat = this.settings.material;", + " uniforms = THREE.UniformsUtils.merge([", + " THREE.UniformsLib.common,", + " THREE.UniformsLib.envmap,", + " THREE.UniformsLib.aomap,", + " THREE.UniformsLib.lightmap,", + " THREE.UniformsLib.emissivemap,", + " THREE.UniformsLib.bumpmap,", + " THREE.UniformsLib.normalmap,", + " THREE.UniformsLib.displacementmap,", + " THREE.UniformsLib.roughnessmap,", + " THREE.UniformsLib.metalnessmap,", + " THREE.UniformsLib.fog,", + " THREE.UniformsLib.lights,", + " {", + " emissive: { value: new THREE.Color(0x000000) },", + " roughness: { value: 1.0 },", + " metalness: { value: 0.0 },", + " envMapIntensity: { value: 1 },", + " },", + " ]);", + " uniforms['diffuse'].value = mat.color;", + " uniforms['opacity'].value = mat.opacity;", + " uniforms['emissive'].value = mat.emissive;", + " uniforms['roughness'].value = mat.roughness;", + " uniforms['metalness'].value = mat.metalness;", + " if (mat.envMap) {", + " uniforms['envMap'].value = mat.envMap;", + " uniforms['envMapIntensity'].value = mat.envMapIntensity;", + " }", + " if (mat.normalMap) {", + " uniforms['normalMap'].value = mat.normalMap;", + " uniforms['normalScale'].value = mat.normalScale;", + " }", + " if (mat.roughnessMap) {", + " uniforms['roughnessMap'].value = mat.roughnessMap;", + " }", + " if (mat.metalnessMap) {", + " uniforms['metalnessMap'].value = mat.metalnessMap;", + " }", + " if (mat.map) {", + " uniforms['map'] = new THREE.Uniform(mat.map);", + " }", + " }", + " else {", + " uniforms = {};", + " uniforms['map'] = new THREE.Uniform(this.settings.material.map);", + " }", + " if (this.settings.material.alphaTest) {", + " defines['USE_ALPHATEST'] = '';", + " uniforms['alphaTest'] = new THREE.Uniform(this.settings.material.alphaTest);", + " }", + " defines['USE_UV'] = '';", + " const uTileCount = this.settings.uTileCount;", + " const vTileCount = this.settings.vTileCount;", + " if (uTileCount > 1 || vTileCount > 1) {", + " defines['UV_TILE'] = '';", + " uniforms['tileCount'] = new THREE.Uniform(new THREE.Vector2(uTileCount, vTileCount));", + " }", + " if (this.settings.material.defines &&", + " this.settings.material.defines['USE_COLOR_AS_ALPHA'] !== undefined) {", + " defines['USE_COLOR_AS_ALPHA'] = '';", + " }", + " if (this.settings.material.normalMap) {", + " defines['USE_NORMALMAP'] = '';", + " defines['NORMALMAP_UV'] = getMaterialUVChannelName(this.settings.material.normalMap.channel);", + " uniforms['normalMapTransform'] = new THREE.Uniform(new THREE.Matrix3().copy(this.settings.material.normalMap.matrix));", + " }", + " if (this.settings.material.map) {", + " defines['USE_MAP'] = '';", + " defines['MAP_UV'] = getMaterialUVChannelName(this.settings.material.map.channel);", + " uniforms['mapTransform'] = new THREE.Uniform(new THREE.Matrix3().copy(this.settings.material.map.matrix));", + " }", + " defines['USE_COLOR_ALPHA'] = '';", + " let needLights = false;", + " if (this.settings.renderMode === RenderMode.BillBoard ||", + " this.settings.renderMode === RenderMode.VerticalBillBoard ||", + " this.settings.renderMode === RenderMode.HorizontalBillBoard ||", + " this.settings.renderMode === RenderMode.Mesh) {", + " let vertexShader;", + " let fragmentShader;", + " if (this.settings.renderMode === RenderMode.Mesh) {", + " if (this.settings.material.type === 'MeshStandardMaterial' ||", + " this.settings.material.type === 'MeshPhysicalMaterial') {", + " defines['USE_COLOR'] = '';", + " vertexShader = local_particle_physics_vert;", + " fragmentShader = particle_physics_frag;", + " needLights = true;", + " }", + " else {", + " vertexShader = local_particle_vert;", + " fragmentShader = particle_frag;", + " }", + " }", + " else {", + " vertexShader = particle_vert;", + " fragmentShader = particle_frag;", + " }", + " if (this.settings.renderMode === RenderMode.VerticalBillBoard) {", + " defines['VERTICAL'] = '';", + " }", + " else if (this.settings.renderMode === RenderMode.HorizontalBillBoard) {", + " defines['HORIZONTAL'] = '';", + " }", + " this.material = new THREE.ShaderMaterial({", + " uniforms: uniforms,", + " defines: defines,", + " vertexShader: vertexShader,", + " fragmentShader: fragmentShader,", + " transparent: this.settings.material.transparent,", + " depthWrite: !this.settings.material.transparent,", + " blending: this.settings.material.blending,", + " side: this.settings.material.side,", + " alphaTest: this.settings.material.alphaTest,", + " lights: needLights,", + " });", + " }", + " else if (this.settings.renderMode === RenderMode.StretchedBillBoard) {", + " uniforms['speedFactor'] = new THREE.Uniform(1.0);", + " this.material = new THREE.ShaderMaterial({", + " uniforms: uniforms,", + " defines: defines,", + " vertexShader: stretched_bb_particle_vert,", + " fragmentShader: particle_frag,", + " transparent: this.settings.material.transparent,", + " depthWrite: !this.settings.material.transparent,", + " blending: this.settings.material.blending,", + " side: this.settings.material.side,", + " alphaTest: this.settings.material.alphaTest,", + " });", + " }", + " else {", + " throw new Error('render mode unavailable');", + " }", + " }", + " update() {", + " let index = 0;", + " let particleCount = 0;", + " this.systems.forEach((system) => {", + " particleCount += system.particleNum;", + " });", + " if (particleCount > this.maxParticles) {", + " this.expandBuffers(particleCount);", + " }", + " this.systems.forEach((system) => {", + " const particles = system.particles;", + " const particleNum = system.particleNum;", + " const rotation = this.quaternion2_;", + " const translation = this.vector2_;", + " const scale = this.vector3_;", + " system.emitter.matrixWorld.decompose(translation, rotation, scale);", + " this.rotationMat_.setFromMatrix4(system.emitter.matrixWorld);", + " for (let j = 0; j < particleNum; j++ , index++) {", + " const particle = particles[j];", + " if (this.settings.renderMode === RenderMode.Mesh) {", + " let q;", + " if (system.worldSpace) {", + " q = particle.rotation;", + " }", + " else {", + " let parentQ;", + " if (particle.parentMatrix) {", + " parentQ = this.quaternion3_.setFromRotationMatrix(particle.parentMatrix);", + " }", + " else {", + " parentQ = rotation;", + " }", + " q = this.quaternion_;", + " q.copy(particle.rotation).multiply(parentQ);", + " }", + " this.rotationBuffer.setXYZW(index, q.x, q.y, q.z, q.w);", + " }", + " else if (this.settings.renderMode === RenderMode.StretchedBillBoard ||", + " this.settings.renderMode === RenderMode.VerticalBillBoard ||", + " this.settings.renderMode === RenderMode.HorizontalBillBoard ||", + " this.settings.renderMode === RenderMode.BillBoard) {", + " this.rotationBuffer.setX(index, particle.rotation);", + " }", + " let vec;", + " if (system.worldSpace) {", + " vec = particle.position;", + " }", + " else {", + " vec = this.vector_;", + " if (particle.parentMatrix) {", + " vec.copy(particle.position).applyMatrix4(particle.parentMatrix);", + " }", + " else {", + " vec.copy(particle.position).applyMatrix4(system.emitter.matrixWorld);", + " }", + " }", + " this.offsetBuffer.setXYZ(index, vec.x, vec.y, vec.z);", + " this.colorBuffer.setXYZW(index, particle.color.x, particle.color.y, particle.color.z, particle.color.w);", + " if (system.worldSpace) {", + " this.sizeBuffer.setX(index, particle.size);", + " }", + " else {", + " if (particle.parentMatrix) {", + " this.sizeBuffer.setX(index, particle.size);", + " }", + " else {", + " this.sizeBuffer.setX(index, (particle.size * (Math.abs(scale.x) + Math.abs(scale.y) + Math.abs(scale.z))) / 3);", + " }", + " }", + " this.uvTileBuffer.setX(index, particle.uvTile);", + " if (this.settings.renderMode === RenderMode.StretchedBillBoard && this.velocityBuffer) {", + " let speedFactor = system.rendererEmitterSettings.speedFactor;", + " if (speedFactor === 0)", + " speedFactor = 0.001;", + " const lengthFactor = system.rendererEmitterSettings.lengthFactor;", + " let vec;", + " if (system.worldSpace) {", + " vec = particle.velocity;", + " }", + " else {", + " vec = this.vector_;", + " if (particle.parentMatrix) {", + " this.rotationMat2_.setFromMatrix4(particle.parentMatrix);", + " vec.copy(particle.velocity).applyMatrix3(this.rotationMat2_);", + " }", + " else {", + " vec.copy(particle.velocity).applyMatrix3(this.rotationMat_);", + " }", + " }", + " this.velocityBuffer.setXYZW(index, vec.x * speedFactor, vec.y * speedFactor, vec.z * speedFactor, lengthFactor);", + " }", + " }", + " });", + " this.geometry.instanceCount = index;", + " if (index > 0) {", + " this.offsetBuffer.clearUpdateRanges();", + " this.offsetBuffer.addUpdateRange(0, index * 3);", + " this.offsetBuffer.needsUpdate = true;", + " this.sizeBuffer.clearUpdateRanges();", + " this.sizeBuffer.addUpdateRange(0, index);", + " this.sizeBuffer.needsUpdate = true;", + " this.colorBuffer.clearUpdateRanges();", + " this.colorBuffer.addUpdateRange(0, index * 4);", + " this.colorBuffer.needsUpdate = true;", + " this.uvTileBuffer.clearUpdateRanges();", + " this.uvTileBuffer.addUpdateRange(0, index);", + " this.uvTileBuffer.needsUpdate = true;", + " if (this.settings.renderMode === RenderMode.StretchedBillBoard && this.velocityBuffer) {", + " this.velocityBuffer.clearUpdateRanges();", + " this.velocityBuffer.addUpdateRange(0, index * 4);", + " this.velocityBuffer.needsUpdate = true;", + " }", + " if (this.settings.renderMode === RenderMode.Mesh) {", + " this.rotationBuffer.clearUpdateRanges();", + " this.rotationBuffer.addUpdateRange(0, index * 4);", + " this.rotationBuffer.needsUpdate = true;", + " }", + " else if (this.settings.renderMode === RenderMode.StretchedBillBoard ||", + " this.settings.renderMode === RenderMode.HorizontalBillBoard ||", + " this.settings.renderMode === RenderMode.VerticalBillBoard ||", + " this.settings.renderMode === RenderMode.BillBoard) {", + " this.rotationBuffer.clearUpdateRanges();", + " this.rotationBuffer.addUpdateRange(0, index);", + " this.rotationBuffer.needsUpdate = true;", + " }", + " }", + " }", + " dispose() {", + " this.geometry.dispose();", + " }", + "}", + "", + "var trail_frag = `", + "", + "#include ", + "#include ", + "#include ", + "#include ", + "#include ", + "#include ", + "", + "uniform sampler2D alphaMap;", + "uniform float useAlphaMap;", + "uniform float visibility;", + "uniform float alphaTest;", + "uniform vec2 repeat;", + "", + "varying vec4 vColor;", + " ", + "void main() {", + " #include ", + " #include ", + "", + " vec4 c = vColor;", + " ", + " #ifdef USE_MAP", + " #ifdef USE_COLOR_AS_ALPHA", + " vec4 tex = texture2D( map, vUv * repeat );", + " c *= vec4(tex.rgb, tex.r);", + " #else", + " c *= texture2D( map, vUv * repeat );", + " #endif", + " #endif", + " if( useAlphaMap == 1. ) c.a *= texture2D( alphaMap, vUv * repeat ).a;", + " if( c.a < alphaTest ) discard;", + " gl_FragColor = c;", + "", + " #include ", + " #include ", + "}`;", + "", + "var trail_vert = `", + "#include ", + "#include ", + "#include ", + "#include ", + "#include ", + "#include ", + "", + "attribute vec3 previous;", + "attribute vec3 next;", + "attribute float side;", + "attribute float width;", + "", + "uniform vec2 resolution;", + "uniform float lineWidth;", + "uniform float sizeAttenuation;", + " ", + "vec2 fix(vec4 i, float aspect) {", + " vec2 res = i.xy / i.w;", + " res.x *= aspect;", + " return res;", + "}", + " ", + "void main() {", + "", + " ${uv_vertex_tile}", + " ", + " float aspect = resolution.x / resolution.y;", + "", + " vColor = color;", + "", + " mat4 m = projectionMatrix * modelViewMatrix;", + " vec4 finalPosition = m * vec4( position, 1.0 );", + " vec4 prevPos = m * vec4( previous, 1.0 );", + " vec4 nextPos = m * vec4( next, 1.0 );", + "", + " vec2 currentP = fix( finalPosition, aspect );", + " vec2 prevP = fix( prevPos, aspect );", + " vec2 nextP = fix( nextPos, aspect );", + "", + " float w = lineWidth * width;", + "", + " vec2 dir;", + " if( nextP == currentP ) dir = normalize( currentP - prevP );", + " else if( prevP == currentP ) dir = normalize( nextP - currentP );", + " else {", + " vec2 dir1 = normalize( currentP - prevP );", + " vec2 dir2 = normalize( nextP - currentP );", + " dir = normalize( dir1 + dir2 );", + "", + " vec2 perp = vec2( -dir1.y, dir1.x );", + " vec2 miter = vec2( -dir.y, dir.x );", + " //w = clamp( w / dot( miter, perp ), 0., 4., * lineWidth * width );", + "", + " }", + "", + " //vec2 normal = ( cross( vec3( dir, 0. ) vec3( 0., 0., 1. ) ) ).xy;", + " vec4 normal = vec4( -dir.y, dir.x, 0., 1. );", + " normal.xy *= .5 * w;", + " normal *= projectionMatrix;", + " if( sizeAttenuation == 0. ) {", + " normal.xy *= finalPosition.w;", + " normal.xy /= ( vec4( resolution, 0., 1. ) * projectionMatrix ).xy;", + " }", + "", + " finalPosition.xy += normal.xy * side;", + "", + " gl_Position = finalPosition;", + "", + "\t#include ", + "\t#include ", + "\t", + " vec4 mvPosition = modelViewMatrix * vec4( position, 1.0 );", + " ", + "\t#include ", + "}`;", + "", + "new THREE.Vector3(0, 0, 1);", + "class TrailBatch extends VFXBatch {", + " constructor(settings) {", + " super(settings);", + " this.vector_ = new THREE.Vector3();", + " this.vector2_ = new THREE.Vector3();", + " this.vector3_ = new THREE.Vector3();", + " this.quaternion_ = new THREE.Quaternion();", + " this.maxParticles = 10000;", + " this.setupBuffers();", + " this.rebuildMaterial();", + " }", + " setupBuffers() {", + " if (this.geometry)", + " this.geometry.dispose();", + " this.geometry = new THREE.BufferGeometry();", + " this.indexBuffer = new THREE.BufferAttribute(new Uint32Array(this.maxParticles * 6), 1);", + " this.indexBuffer.setUsage(THREE.DynamicDrawUsage);", + " this.geometry.setIndex(this.indexBuffer);", + " this.positionBuffer = new THREE.BufferAttribute(new Float32Array(this.maxParticles * 6), 3);", + " this.positionBuffer.setUsage(THREE.DynamicDrawUsage);", + " this.geometry.setAttribute('position', this.positionBuffer);", + " this.previousBuffer = new THREE.BufferAttribute(new Float32Array(this.maxParticles * 6), 3);", + " this.previousBuffer.setUsage(THREE.DynamicDrawUsage);", + " this.geometry.setAttribute('previous', this.previousBuffer);", + " this.nextBuffer = new THREE.BufferAttribute(new Float32Array(this.maxParticles * 6), 3);", + " this.nextBuffer.setUsage(THREE.DynamicDrawUsage);", + " this.geometry.setAttribute('next', this.nextBuffer);", + " this.widthBuffer = new THREE.BufferAttribute(new Float32Array(this.maxParticles * 2), 1);", + " this.widthBuffer.setUsage(THREE.DynamicDrawUsage);", + " this.geometry.setAttribute('width', this.widthBuffer);", + " this.sideBuffer = new THREE.BufferAttribute(new Float32Array(this.maxParticles * 2), 1);", + " this.sideBuffer.setUsage(THREE.DynamicDrawUsage);", + " this.geometry.setAttribute('side', this.sideBuffer);", + " this.uvBuffer = new THREE.BufferAttribute(new Float32Array(this.maxParticles * 4), 2);", + " this.uvBuffer.setUsage(THREE.DynamicDrawUsage);", + " this.geometry.setAttribute('uv', this.uvBuffer);", + " this.colorBuffer = new THREE.BufferAttribute(new Float32Array(this.maxParticles * 8), 4);", + " this.colorBuffer.setUsage(THREE.DynamicDrawUsage);", + " this.geometry.setAttribute('color', this.colorBuffer);", + " }", + " expandBuffers(target) {", + " while (target >= this.maxParticles) {", + " this.maxParticles *= 2;", + " }", + " this.setupBuffers();", + " }", + " rebuildMaterial() {", + " this.layers.mask = this.settings.layers.mask;", + " const uniforms = {", + " lineWidth: { value: 1 },", + " map: { value: null },", + " useMap: { value: 0 },", + " alphaMap: { value: null },", + " useAlphaMap: { value: 0 },", + " resolution: { value: new THREE.Vector2(1, 1) },", + " sizeAttenuation: { value: 1 },", + " visibility: { value: 1 },", + " alphaTest: { value: 0 },", + " repeat: { value: new THREE.Vector2(1, 1) },", + " };", + " const defines = {};", + " defines['USE_UV'] = '';", + " defines['USE_COLOR_ALPHA'] = '';", + " if (this.settings.material.map) {", + " defines['USE_MAP'] = '';", + " defines['MAP_UV'] = getMaterialUVChannelName(this.settings.material.map.channel);", + " uniforms['map'] = new THREE.Uniform(this.settings.material.map);", + " uniforms['mapTransform'] = new THREE.Uniform(new THREE.Matrix3().copy(this.settings.material.map.matrix));", + " }", + " if (this.settings.material.defines &&", + " this.settings.material.defines['USE_COLOR_AS_ALPHA'] !== undefined) {", + " defines['USE_COLOR_AS_ALPHA'] = '';", + " }", + " if (this.settings.renderMode === RenderMode.Trail) {", + " this.material = new THREE.ShaderMaterial({", + " uniforms: uniforms,", + " defines: defines,", + " vertexShader: trail_vert,", + " fragmentShader: trail_frag,", + " transparent: this.settings.material.transparent,", + " depthWrite: !this.settings.material.transparent,", + " side: this.settings.material.side,", + " blending: this.settings.material.blending || THREE.AdditiveBlending,", + " });", + " }", + " else {", + " throw new Error('render mode unavailable');", + " }", + " }", + " update() {", + " let index = 0;", + " let triangles = 0;", + " let particleCount = 0;", + " this.systems.forEach((system) => {", + " for (let j = 0; j < system.particleNum; j++) {", + " particleCount += system.particles[j].previous.length * 2;", + " }", + " });", + " if (particleCount > this.maxParticles) {", + " this.expandBuffers(particleCount);", + " }", + " this.systems.forEach((system) => {", + " const rotation = this.quaternion_;", + " const translation = this.vector2_;", + " const scale = this.vector3_;", + " system.emitter.matrixWorld.decompose(translation, rotation, scale);", + " const particles = system.particles;", + " const particleNum = system.particleNum;", + " const uTileCount = this.settings.uTileCount;", + " const vTileCount = this.settings.vTileCount;", + " const tileWidth = 1 / uTileCount;", + " const tileHeight = 1 / vTileCount;", + " for (let j = 0; j < particleNum; j++) {", + " const particle = particles[j];", + " const col = particle.uvTile % vTileCount;", + " const row = Math.floor(particle.uvTile / vTileCount + 0.001);", + " const iter = particle.previous.values();", + " let curIter = iter.next();", + " let previous = curIter.value;", + " let current = previous;", + " if (!curIter.done)", + " curIter = iter.next();", + " let next;", + " if (curIter.value !== undefined) {", + " next = curIter.value;", + " }", + " else {", + " next = current;", + " }", + " for (let i = 0; i < particle.previous.length; i++ , index += 2) {", + " this.positionBuffer.setXYZ(index, current.position.x, current.position.y, current.position.z);", + " this.positionBuffer.setXYZ(index + 1, current.position.x, current.position.y, current.position.z);", + " if (system.worldSpace) {", + " this.positionBuffer.setXYZ(index, current.position.x, current.position.y, current.position.z);", + " this.positionBuffer.setXYZ(index + 1, current.position.x, current.position.y, current.position.z);", + " }", + " else {", + " if (particle.parentMatrix) {", + " this.vector_.copy(current.position).applyMatrix4(particle.parentMatrix);", + " }", + " else {", + " this.vector_.copy(current.position).applyMatrix4(system.emitter.matrixWorld);", + " }", + " this.positionBuffer.setXYZ(index, this.vector_.x, this.vector_.y, this.vector_.z);", + " this.positionBuffer.setXYZ(index + 1, this.vector_.x, this.vector_.y, this.vector_.z);", + " }", + " if (system.worldSpace) {", + " this.previousBuffer.setXYZ(index, previous.position.x, previous.position.y, previous.position.z);", + " this.previousBuffer.setXYZ(index + 1, previous.position.x, previous.position.y, previous.position.z);", + " }", + " else {", + " if (particle.parentMatrix) {", + " this.vector_.copy(previous.position).applyMatrix4(particle.parentMatrix);", + " }", + " else {", + " this.vector_.copy(previous.position).applyMatrix4(system.emitter.matrixWorld);", + " }", + " this.previousBuffer.setXYZ(index, this.vector_.x, this.vector_.y, this.vector_.z);", + " this.previousBuffer.setXYZ(index + 1, this.vector_.x, this.vector_.y, this.vector_.z);", + " }", + " if (system.worldSpace) {", + " this.nextBuffer.setXYZ(index, next.position.x, next.position.y, next.position.z);", + " this.nextBuffer.setXYZ(index + 1, next.position.x, next.position.y, next.position.z);", + " }", + " else {", + " if (particle.parentMatrix) {", + " this.vector_.copy(next.position).applyMatrix4(particle.parentMatrix);", + " }", + " else {", + " this.vector_.copy(next.position).applyMatrix4(system.emitter.matrixWorld);", + " }", + " this.nextBuffer.setXYZ(index, this.vector_.x, this.vector_.y, this.vector_.z);", + " this.nextBuffer.setXYZ(index + 1, this.vector_.x, this.vector_.y, this.vector_.z);", + " }", + " this.sideBuffer.setX(index, -1);", + " this.sideBuffer.setX(index + 1, 1);", + " if (system.worldSpace) {", + " this.widthBuffer.setX(index, current.size);", + " this.widthBuffer.setX(index + 1, current.size);", + " }", + " else {", + " if (particle.parentMatrix) {", + " this.widthBuffer.setX(index, current.size);", + " this.widthBuffer.setX(index + 1, current.size);", + " }", + " else {", + " const objectScale = (Math.abs(scale.x) + Math.abs(scale.y) + Math.abs(scale.z)) / 3;", + " this.widthBuffer.setX(index, current.size * objectScale);", + " this.widthBuffer.setX(index + 1, current.size * objectScale);", + " }", + " }", + " this.uvBuffer.setXY(index, (i / particle.previous.length + col) * tileWidth, (vTileCount - row - 1) * tileHeight);", + " this.uvBuffer.setXY(index + 1, (i / particle.previous.length + col) * tileWidth, (vTileCount - row) * tileHeight);", + " this.colorBuffer.setXYZW(index, current.color.x, current.color.y, current.color.z, current.color.w);", + " this.colorBuffer.setXYZW(index + 1, current.color.x, current.color.y, current.color.z, current.color.w);", + " if (i + 1 < particle.previous.length) {", + " this.indexBuffer.setX(triangles * 3, index);", + " this.indexBuffer.setX(triangles * 3 + 1, index + 1);", + " this.indexBuffer.setX(triangles * 3 + 2, index + 2);", + " triangles++;", + " this.indexBuffer.setX(triangles * 3, index + 2);", + " this.indexBuffer.setX(triangles * 3 + 1, index + 1);", + " this.indexBuffer.setX(triangles * 3 + 2, index + 3);", + " triangles++;", + " }", + " previous = current;", + " current = next;", + " if (!curIter.done) {", + " curIter = iter.next();", + " if (curIter.value !== undefined) {", + " next = curIter.value;", + " }", + " }", + " }", + " }", + " });", + " this.positionBuffer.clearUpdateRanges();", + " this.positionBuffer.addUpdateRange(0, index * 3);", + " this.positionBuffer.needsUpdate = true;", + " this.previousBuffer.clearUpdateRanges();", + " this.previousBuffer.addUpdateRange(0, index * 3);", + " this.previousBuffer.needsUpdate = true;", + " this.nextBuffer.clearUpdateRanges();", + " this.nextBuffer.addUpdateRange(0, index * 3);", + " this.nextBuffer.needsUpdate = true;", + " this.sideBuffer.clearUpdateRanges();", + " this.sideBuffer.addUpdateRange(0, index);", + " this.sideBuffer.needsUpdate = true;", + " this.widthBuffer.clearUpdateRanges();", + " this.widthBuffer.addUpdateRange(0, index);", + " this.widthBuffer.needsUpdate = true;", + " this.uvBuffer.clearUpdateRanges();", + " this.uvBuffer.addUpdateRange(0, index * 2);", + " this.uvBuffer.needsUpdate = true;", + " this.colorBuffer.clearUpdateRanges();", + " this.colorBuffer.addUpdateRange(0, index * 4);", + " this.colorBuffer.needsUpdate = true;", + " this.indexBuffer.clearUpdateRanges();", + " this.indexBuffer.addUpdateRange(0, triangles * 3);", + " this.indexBuffer.needsUpdate = true;", + " this.geometry.setDrawRange(0, triangles * 3);", + " }", + " dispose() {", + " this.geometry.dispose();", + " }", + "}", + "", + "class BatchedRenderer extends THREE.Object3D {", + " constructor() {", + " super();", + " this.batches = [];", + " this.systemToBatchIndex = new Map();", + " this.type = 'BatchedRenderer';", + " }", + " static equals(a, b) {", + " return (a.material.side === b.material.side &&", + " a.material.blending === b.material.blending &&", + " a.material.transparent === b.material.transparent &&", + " a.material.type === b.material.type &&", + " a.material.alphaTest === b.material.alphaTest &&", + " a.material.map === b.material.map &&", + " a.renderMode === b.renderMode &&", + " a.uTileCount === b.uTileCount &&", + " a.vTileCount === b.vTileCount &&", + " a.instancingGeometry === b.instancingGeometry &&", + " a.renderOrder === b.renderOrder &&", + " a.layers.mask === b.layers.mask);", + " }", + " addSystem(system) {", + " system._renderer = this;", + " const settings = system.getRendererSettings();", + " for (let i = 0; i < this.batches.length; i++) {", + " if (BatchedRenderer.equals(this.batches[i].settings, settings)) {", + " this.batches[i].addSystem(system);", + " this.systemToBatchIndex.set(system, i);", + " return;", + " }", + " }", + " let batch;", + " switch (settings.renderMode) {", + " case RenderMode.Trail:", + " batch = new TrailBatch(settings);", + " break;", + " case RenderMode.Mesh:", + " case RenderMode.BillBoard:", + " case RenderMode.VerticalBillBoard:", + " case RenderMode.HorizontalBillBoard:", + " case RenderMode.StretchedBillBoard:", + " batch = new SpriteBatch(settings);", + " break;", + " }", + " batch.addSystem(system);", + " this.batches.push(batch);", + " this.systemToBatchIndex.set(system, this.batches.length - 1);", + " this.add(batch);", + " }", + " deleteSystem(system) {", + " const batchIndex = this.systemToBatchIndex.get(system);", + " if (batchIndex != undefined) {", + " this.batches[batchIndex].removeSystem(system);", + " this.systemToBatchIndex.delete(system);", + " }", + " }", + " updateSystem(system) {", + " this.deleteSystem(system);", + " this.addSystem(system);", + " }", + " update(delta) {", + " this.systemToBatchIndex.forEach((value, ps) => {", + " ps.update(delta);", + " });", + " for (let i = 0; i < this.batches.length; i++) {", + " this.batches[i].update();", + " }", + " }", + "}", + "", + "const BatchedParticleRenderer = BatchedRenderer;", + "", + "class QuarksLoader extends THREE.ObjectLoader {", + " constructor(manager) {", + " super(manager);", + " }", + " linkReference(object) {", + " const objectsMap = {};", + " object.traverse(function (child) {", + " objectsMap[child.uuid] = child;", + " });", + " object.traverse(function (child) {", + " if (child.type === 'ParticleEmitter') {", + " const system = child.system;", + " system.emitterShape;", + " for (let i = 0; i < system.behaviors.length; i++) {", + " if (system.behaviors[i] instanceof EmitSubParticleSystem) {", + " system.behaviors[i].subParticleSystem = objectsMap[system.behaviors[i].subParticleSystem];", + " }", + " }", + " }", + " });", + " }", + " parse(json, onLoad) {", + " const object = super.parse(json, onLoad);", + " this.linkReference(object);", + " return object;", + " }", + " parseObject(data, geometries, materials, textures, animations) {", + " let object;", + " function getGeometry(name) {", + " if (geometries[name] === undefined) {", + " console.warn('THREE.ObjectLoader: Undefined geometry', name);", + " }", + " return geometries[name];", + " }", + " function getMaterial(name) {", + " if (name === undefined)", + " return undefined;", + " if (Array.isArray(name)) {", + " const array = [];", + " for (let i = 0, l = name.length; i < l; i++) {", + " const uuid = name[i];", + " if (materials[uuid] === undefined) {", + " console.warn('THREE.ObjectLoader: Undefined material', uuid);", + " }", + " array.push(materials[uuid]);", + " }", + " return array;", + " }", + " if (materials[name] === undefined) {", + " console.warn('THREE.ObjectLoader: Undefined material', name);", + " }", + " return materials[name];", + " }", + " function getTexture(uuid) {", + " if (textures[uuid] === undefined) {", + " console.warn('THREE.ObjectLoader: Undefined texture', uuid);", + " }", + " return textures[uuid];", + " }", + " let geometry, material;", + " const meta = {", + " textures: textures,", + " geometries: geometries,", + " materials: materials,", + " };", + " const dependencies = {};", + " switch (data.type) {", + " case 'ParticleEmitter':", + " object = ParticleSystem.fromJSON(data.ps, meta, dependencies).emitter;", + " break;", + " case 'Scene':", + " object = new THREE.Scene();", + " if (data.background !== undefined) {", + " if (Number.isInteger(data.background)) {", + " object.background = new THREE.Color(data.background);", + " }", + " else {", + " object.background = getTexture(data.background);", + " }", + " }", + " if (data.environment !== undefined) {", + " object.environment = getTexture(data.environment);", + " }", + " if (data.fog !== undefined) {", + " if (data.fog.type === 'Fog') {", + " object.fog = new THREE.Fog(data.fog.color, data.fog.near, data.fog.far);", + " }", + " else if (data.fog.type === 'FogExp2') {", + " object.fog = new THREE.FogExp2(data.fog.color, data.fog.density);", + " }", + " }", + " if (data.backgroundBlurriness !== undefined)", + " object.backgroundBlurriness = data.backgroundBlurriness;", + " break;", + " case 'PerspectiveCamera':", + " object = new THREE.PerspectiveCamera(data.fov, data.aspect, data.near, data.far);", + " if (data.focus !== undefined)", + " object.focus = data.focus;", + " if (data.zoom !== undefined)", + " object.zoom = data.zoom;", + " if (data.filmGauge !== undefined)", + " object.filmGauge = data.filmGauge;", + " if (data.filmOffset !== undefined)", + " object.filmOffset = data.filmOffset;", + " if (data.view !== undefined)", + " object.view = Object.assign({}, data.view);", + " break;", + " case 'OrthographicCamera':", + " object = new THREE.OrthographicCamera(data.left, data.right, data.top, data.bottom, data.near, data.far);", + " if (data.zoom !== undefined)", + " object.zoom = data.zoom;", + " if (data.view !== undefined)", + " object.view = Object.assign({}, data.view);", + " break;", + " case 'AmbientLight':", + " object = new THREE.AmbientLight(data.color, data.intensity);", + " break;", + " case 'DirectionalLight':", + " object = new THREE.DirectionalLight(data.color, data.intensity);", + " break;", + " case 'PointLight':", + " object = new THREE.PointLight(data.color, data.intensity, data.distance, data.decay);", + " break;", + " case 'RectAreaLight':", + " object = new THREE.RectAreaLight(data.color, data.intensity, data.width, data.height);", + " break;", + " case 'SpotLight':", + " object = new THREE.SpotLight(data.color, data.intensity, data.distance, data.angle, data.penumbra, data.decay);", + " break;", + " case 'HemisphereLight':", + " object = new THREE.HemisphereLight(data.color, data.groundColor, data.intensity);", + " break;", + " case 'LightProbe':", + " object = new THREE.LightProbe().fromJSON(data);", + " break;", + " case 'SkinnedMesh':", + " geometry = getGeometry(data.geometry);", + " material = getMaterial(data.material);", + " object = new THREE.SkinnedMesh(geometry, material);", + " if (data.bindMode !== undefined)", + " object.bindMode = data.bindMode;", + " if (data.bindMatrix !== undefined)", + " object.bindMatrix.fromArray(data.bindMatrix);", + " if (data.skeleton !== undefined)", + " object.skeleton = data.skeleton;", + " break;", + " case 'Mesh':", + " geometry = getGeometry(data.geometry);", + " material = getMaterial(data.material);", + " object = new THREE.Mesh(geometry, material);", + " break;", + " case 'InstancedMesh': {", + " geometry = getGeometry(data.geometry);", + " material = getMaterial(data.material);", + " const count = data.count;", + " const instanceMatrix = data.instanceMatrix;", + " const instanceColor = data.instanceColor;", + " object = new THREE.InstancedMesh(geometry, material, count);", + " object.instanceMatrix = new THREE.InstancedBufferAttribute(new Float32Array(instanceMatrix.array), 16);", + " if (instanceColor !== undefined)", + " object.instanceColor = new THREE.InstancedBufferAttribute(new Float32Array(instanceColor.array), instanceColor.itemSize);", + " break;", + " }", + " case 'LOD':", + " object = new THREE.LOD();", + " break;", + " case 'Line':", + " object = new THREE.Line(getGeometry(data.geometry), getMaterial(data.material));", + " break;", + " case 'LineLoop':", + " object = new THREE.LineLoop(getGeometry(data.geometry), getMaterial(data.material));", + " break;", + " case 'LineSegments':", + " object = new THREE.LineSegments(getGeometry(data.geometry), getMaterial(data.material));", + " break;", + " case 'PointCloud':", + " case 'Points':", + " object = new THREE.Points(getGeometry(data.geometry), getMaterial(data.material));", + " break;", + " case 'Sprite':", + " object = new THREE.Sprite(getMaterial(data.material));", + " break;", + " case 'Group':", + " object = new THREE.Group();", + " break;", + " case 'Bone':", + " object = new THREE.Bone();", + " break;", + " default:", + " object = new THREE.Object3D();", + " }", + " object.uuid = data.uuid;", + " if (data.name !== undefined)", + " object.name = data.name;", + " if (data.matrix !== undefined) {", + " object.matrix.fromArray(data.matrix);", + " if (data.matrixAutoUpdate !== undefined)", + " object.matrixAutoUpdate = data.matrixAutoUpdate;", + " if (object.matrixAutoUpdate)", + " object.matrix.decompose(object.position, object.quaternion, object.scale);", + " }", + " else {", + " if (data.position !== undefined)", + " object.position.fromArray(data.position);", + " if (data.rotation !== undefined)", + " object.rotation.fromArray(data.rotation);", + " if (data.quaternion !== undefined)", + " object.quaternion.fromArray(data.quaternion);", + " if (data.scale !== undefined)", + " object.scale.fromArray(data.scale);", + " }", + " if (data.castShadow !== undefined)", + " object.castShadow = data.castShadow;", + " if (data.receiveShadow !== undefined)", + " object.receiveShadow = data.receiveShadow;", + " if (data.shadow) {", + " if (data.shadow.bias !== undefined)", + " object.shadow.bias = data.shadow.bias;", + " if (data.shadow.normalBias !== undefined)", + " object.normalBias = data.shadow.normalBias;", + " if (data.shadow.radius !== undefined)", + " object.radius = data.shadow.radius;", + " if (data.shadow.mapSize !== undefined)", + " object.mapSize.fromArray(data.shadow.mapSize);", + " if (data.shadow.camera !== undefined) {", + " object.camera = this.parseObject(data.shadow.camera);", + " }", + " }", + " if (data.visible !== undefined)", + " object.visible = data.visible;", + " if (data.frustumCulled !== undefined)", + " object.frustumCulled = data.frustumCulled;", + " if (data.renderOrder !== undefined)", + " object.renderOrder = data.renderOrder;", + " if (data.userData !== undefined)", + " object.userData = data.userData;", + " if (data.layers !== undefined)", + " object.layers.mask = data.layers;", + " if (data.children !== undefined) {", + " const children = data.children;", + " for (let i = 0; i < children.length; i++) {", + " object.add(this.parseObject(children[i], geometries, materials, textures, animations));", + " }", + " }", + " if (data.animations !== undefined) {", + " const objectAnimations = data.animations;", + " for (let i = 0; i < objectAnimations.length; i++) {", + " const uuid = objectAnimations[i];", + " object.animations.push(animations[uuid]);", + " }", + " }", + " if (data.type === 'LOD') {", + " if (data.autoUpdate !== undefined)", + " object.autoUpdate = data.autoUpdate;", + " const levels = data.levels;", + " for (let l = 0; l < levels.length; l++) {", + " const level = levels[l];", + " const child = object.getObjectByProperty('uuid', level.object);", + " if (child !== undefined) {", + " object.addLevel(child, level.distance);", + " }", + " }", + " }", + " return object;", + " }", + "}", + "", + "const Plugins = [];", + "function loadPlugin(plugin) {", + " const existing = Plugins.find((item) => item.id === plugin.id);", + " if (!existing) {", + " plugin.initialize();", + " for (const emitterShape of plugin.emitterShapes) {", + " if (!EmitterShapes[emitterShape.type]) {", + " EmitterShapes[emitterShape.type] = emitterShape;", + " }", + " }", + " for (const behavior of plugin.behaviors) {", + " if (!BehaviorTypes[behavior.type]) {", + " BehaviorTypes[behavior.type] = behavior;", + " }", + " }", + " }", + "}", + "function unloadPlugin(pluginId) {", + " const plugin = Plugins.find((item) => item.id === pluginId);", + " if (plugin) {", + " for (const emitterShape of plugin.emitterShapes) {", + " if (EmitterShapes[emitterShape.type]) {", + " delete EmitterShapes[emitterShape.type];", + " }", + " }", + " for (const behavior of plugin.behaviors) {", + " if (BehaviorTypes[behavior.type]) {", + " delete BehaviorTypes[behavior.type];", + " }", + " }", + " }", + "}", + "", + "let NodeValueType = void 0;", + "(function (NodeValueType) {", + " NodeValueType[NodeValueType[\"Number\"] = 0] = \"Number\";", + " NodeValueType[NodeValueType[\"Vec2\"] = 1] = \"Vec2\";", + " NodeValueType[NodeValueType[\"Vec3\"] = 2] = \"Vec3\";", + " NodeValueType[NodeValueType[\"Vec4\"] = 3] = \"Vec4\";", + " NodeValueType[NodeValueType[\"Boolean\"] = 4] = \"Boolean\";", + " NodeValueType[NodeValueType[\"AnyType\"] = 5] = \"AnyType\";", + " NodeValueType[NodeValueType[\"NullableAnyType\"] = 6] = \"NullableAnyType\";", + " NodeValueType[NodeValueType[\"EventStream\"] = 7] = \"EventStream\";", + "})(NodeValueType || (NodeValueType = {}));", + "const genDefaultForNodeValueType = (type) => {", + " switch (type) {", + " case NodeValueType.Boolean:", + " return false;", + " case NodeValueType.Number:", + " return 0;", + " case NodeValueType.Vec2:", + " return new THREE.Vector2();", + " case NodeValueType.Vec3:", + " return new THREE.Vector3();", + " case NodeValueType.Vec4:", + " return new THREE.Vector4();", + " case NodeValueType.AnyType:", + " return 0;", + " case NodeValueType.NullableAnyType:", + " return undefined;", + " }", + "};", + "", + "class Node {", + " constructor(type, signatureIndex = -1, data = {}) {", + " this.inputs = [];", + " this.outputs = [];", + " this.signatureIndex = -1;", + " this.position = new THREE.Vector2();", + " this.outputValues = [];", + " this.id = '' + Math.round(Math.random() * 100000);", + " this.type = type;", + " this.signatureIndex = signatureIndex;", + " this.data = data;", + " const realIndex = signatureIndex === -1 ? 0 : signatureIndex;", + " for (let i = 0; i < type.nodeTypeSignatures[realIndex].inputTypes.length; i++) {", + " this.inputs.push(undefined);", + " }", + " for (let i = 0; i < type.nodeTypeSignatures[realIndex].outputTypes.length; i++) {", + " this.outputs.push([]);", + " this.outputValues.push(genDefaultForNodeValueType(type.nodeTypeSignatures[realIndex].outputTypes[i]));", + " }", + " }", + " get inputTypes() {", + " const signatureIndex = this.signatureIndex === -1 ? 0 : this.signatureIndex;", + " return this.type.nodeTypeSignatures[signatureIndex].inputTypes;", + " }", + " get outputTypes() {", + " const signatureIndex = this.signatureIndex === -1 ? 0 : this.signatureIndex;", + " return this.type.nodeTypeSignatures[signatureIndex].outputTypes;", + " }", + " func(context, inputs, outputs) {", + " const signatureIndex = this.signatureIndex === -1 ? 0 : this.signatureIndex;", + " this.type.nodeTypeSignatures[signatureIndex].func(context, this.data, inputs, outputs);", + " }", + "}", + "class Wire {", + " constructor(input, inputIndex, output, outputIndex) {", + " this.input = input;", + " this.inputIndex = inputIndex;", + " this.input.outputs[inputIndex].push(this);", + " this.output = output;", + " this.outputIndex = outputIndex;", + " this.output.inputs[outputIndex] = this;", + " }", + "}", + "", + "class BaseCompiler {", + " constructor() {", + " this.visited = new Set();", + " BaseCompiler.Instance = this;", + " }", + " buildExecutionOrder(graph, context) {", + " graph.nodesInOrder.length = 0;", + " this.visited.clear();", + " for (let i = 0; i < graph.outputNodes.length; i++) {", + " const node = graph.outputNodes[i];", + " if (node.inputs[0] !== undefined) {", + " this._traverse(node, graph, context);", + " }", + " }", + " graph.compiled = true;", + " }", + " _traverse(node, graph, context) {", + " this.visited.add(node.id);", + " for (let i = 0; i < node.inputs.length; i++) {", + " if (node.inputs[i] instanceof Wire) {", + " const inputNode = node.inputs[i].input;", + " if (!this.visited.has(inputNode.id)) {", + " this._traverse(inputNode, graph, context);", + " }", + " }", + " else if (node.inputs[i] !== undefined);", + " else;", + " }", + " graph.nodesInOrder.push(node);", + " }", + "}", + "", + "class Interpreter extends BaseCompiler {", + " constructor() {", + " super();", + " }", + " executeCompiledGraph(graph, context) {", + " const nodes = graph.nodesInOrder;", + " for (let i = 0; i < nodes.length; i++) {", + " const inputValues = [];", + " const node = nodes[i];", + " for (let j = 0; j < node.inputs.length; j++) {", + " if (node.inputs[j] instanceof Wire) {", + " inputValues.push(node.inputs[j].input.outputValues[node.inputs[j].inputIndex]);", + " }", + " else if (node.inputs[j] !== undefined) {", + " inputValues.push(node.inputs[j].getValue(context));", + " }", + " else {", + " inputValues.push(undefined);", + " }", + " }", + " node.func(context, inputValues, node.outputValues);", + " }", + " }", + " run(graph, context) {", + " if (!graph.compiled) {", + " this.buildExecutionOrder(graph, context);", + " }", + " this.executeCompiledGraph(graph, context);", + " }", + "}", + "", + "class NodeType {", + " constructor(name) {", + " this.nodeTypeSignatures = [];", + " this.name = name;", + " }", + " addSignature(inputTypes, outputTypes, func) {", + " this.nodeTypeSignatures.push({", + " inputTypes: inputTypes,", + " outputTypes: outputTypes,", + " func: func,", + " });", + " }", + "}", + "class GraphNodeType extends NodeType {", + " constructor(nodeGraph) {", + " const inputTypes = [];", + " for (let i = 0; i < nodeGraph.inputNodes.length; i++) {", + " if (nodeGraph.inputNodes[i].type.name === 'input') {", + " inputTypes.push(nodeGraph.inputNodes[i].data.type);", + " }", + " }", + " const outputTypes = [];", + " for (let i = 0; i < nodeGraph.outputNodes.length; i++) {", + " if (nodeGraph.outputNodes[i].type.name === 'output') {", + " outputTypes.push(nodeGraph.outputNodes[i].data.type);", + " }", + " }", + " super(nodeGraph.name);", + " this.addSignature(inputTypes, outputTypes, (context, data, inputs, outputs) => {", + " context.inputs = inputs;", + " context.outputs = outputs;", + " Interpreter.Instance.run(nodeGraph, context);", + " });", + " this.nodeGraph = nodeGraph;", + " }", + "}", + "", + "const NodeTypes = {};", + "const addNode = new NodeType('add');", + "addNode.addSignature([NodeValueType.Number, NodeValueType.Number], [NodeValueType.Number], (context, data, inputs, outputs) => {", + " outputs[0] = inputs[0] + inputs[1];", + "});", + "addNode.addSignature([NodeValueType.Vec2, NodeValueType.Vec2], [NodeValueType.Vec2], (context, data, inputs, outputs) => {", + " outputs[0].addVectors(inputs[0], inputs[1]);", + "});", + "addNode.addSignature([NodeValueType.Vec3, NodeValueType.Vec3], [NodeValueType.Vec3], (context, data, inputs, outputs) => {", + " outputs[0].addVectors(inputs[0], inputs[1]);", + "});", + "addNode.addSignature([NodeValueType.Vec4, NodeValueType.Vec4], [NodeValueType.Vec4], (context, data, inputs, outputs) => {", + " outputs[0].addVectors(inputs[0], inputs[1]);", + "});", + "NodeTypes['add'] = addNode;", + "const subNode = new NodeType('sub');", + "subNode.addSignature([NodeValueType.Number, NodeValueType.Number], [NodeValueType.Number], (context, data, inputs, outputs) => {", + " outputs[0] = inputs[0] - inputs[1];", + "});", + "subNode.addSignature([NodeValueType.Vec2, NodeValueType.Vec2], [NodeValueType.Vec2], (context, data, inputs, outputs) => {", + " outputs[0].subVectors(inputs[0], inputs[1]);", + "});", + "subNode.addSignature([NodeValueType.Vec3, NodeValueType.Vec3], [NodeValueType.Vec3], (context, data, inputs, outputs) => {", + " outputs[0].subVectors(inputs[0], inputs[1]);", + "});", + "subNode.addSignature([NodeValueType.Vec4, NodeValueType.Vec4], [NodeValueType.Vec4], (context, data, inputs, outputs) => {", + " outputs[0].subVectors(inputs[0], inputs[1]);", + "});", + "NodeTypes['sub'] = subNode;", + "const mulNode = new NodeType('mul');", + "mulNode.addSignature([NodeValueType.Number, NodeValueType.Number], [NodeValueType.Number], (context, data, inputs, outputs) => {", + " outputs[0] = inputs[0] * inputs[1];", + "});", + "mulNode.addSignature([NodeValueType.Vec2, NodeValueType.Number], [NodeValueType.Vec2], (context, data, inputs, outputs) => {", + " outputs[0].copy(inputs[0]).multiplyScalar(inputs[1]);", + "});", + "mulNode.addSignature([NodeValueType.Vec3, NodeValueType.Number], [NodeValueType.Vec3], (context, data, inputs, outputs) => {", + " outputs[0].copy(inputs[0]).multiplyScalar(inputs[1]);", + "});", + "mulNode.addSignature([NodeValueType.Vec4, NodeValueType.Number], [NodeValueType.Vec4], (context, data, inputs, outputs) => {", + " outputs[0].copy(inputs[0]).multiplyScalar(inputs[1]);", + "});", + "NodeTypes['mul'] = mulNode;", + "const divNode = new NodeType('div');", + "divNode.addSignature([NodeValueType.Number, NodeValueType.Number], [NodeValueType.Number], (context, data, inputs, outputs) => {", + " outputs[0] = inputs[0] / inputs[1];", + "});", + "divNode.addSignature([NodeValueType.Vec2, NodeValueType.Number], [NodeValueType.Vec2], (context, data, inputs, outputs) => {", + " outputs[0].copy(inputs[0]).divideScalar(inputs[1]);", + "});", + "divNode.addSignature([NodeValueType.Vec3, NodeValueType.Number], [NodeValueType.Vec3], (context, data, inputs, outputs) => {", + " outputs[0].copy(inputs[0]).divideScalar(inputs[1]);", + "});", + "divNode.addSignature([NodeValueType.Vec4, NodeValueType.Number], [NodeValueType.Vec4], (context, data, inputs, outputs) => {", + " outputs[0].copy(inputs[0]).divideScalar(inputs[1]);", + "});", + "NodeTypes['div'] = divNode;", + "const sinNode = new NodeType('sin');", + "sinNode.addSignature([NodeValueType.Number], [NodeValueType.Number], (context, data, inputs, outputs) => {", + " outputs[0] = Math.sin(inputs[0]);", + "});", + "NodeTypes['sin'] = sinNode;", + "const cosNode = new NodeType('cos');", + "cosNode.addSignature([NodeValueType.Number], [NodeValueType.Number], (context, data, inputs, outputs) => {", + " outputs[0] = Math.cos(inputs[0]);", + "});", + "NodeTypes['cos'] = cosNode;", + "const tanNode = new NodeType('tan');", + "tanNode.addSignature([NodeValueType.Number], [NodeValueType.Number], (context, data, inputs, outputs) => {", + " outputs[0] = Math.tan(inputs[0]);", + "});", + "NodeTypes['tan'] = tanNode;", + "const absNode = new NodeType('abs');", + "absNode.addSignature([NodeValueType.Number], [NodeValueType.Number], (context, data, inputs, outputs) => {", + " outputs[0] = Math.abs(inputs[0]);", + "});", + "NodeTypes['abs'] = absNode;", + "const minNode = new NodeType('min');", + "minNode.addSignature([NodeValueType.Number, NodeValueType.Number], [NodeValueType.Number], (context, data, inputs, outputs) => {", + " outputs[0] = Math.min(inputs[0], inputs[1]);", + "});", + "NodeTypes['min'] = minNode;", + "const maxNode = new NodeType('max');", + "maxNode.addSignature([NodeValueType.Number, NodeValueType.Number], [NodeValueType.Number], (context, data, inputs, outputs) => {", + " outputs[0] = Math.max(inputs[0], inputs[1]);", + "});", + "NodeTypes['max'] = maxNode;", + "const dot = new NodeType('dot');", + "dot.addSignature([NodeValueType.Vec2, NodeValueType.Vec2], [NodeValueType.Number], (context, data, inputs, outputs) => {", + " outputs[0] = inputs[0].dot(inputs[1]);", + "});", + "dot.addSignature([NodeValueType.Vec3, NodeValueType.Vec3], [NodeValueType.Number], (context, data, inputs, outputs) => {", + " outputs[0] = inputs[0].dot(inputs[1]);", + "});", + "dot.addSignature([NodeValueType.Vec4, NodeValueType.Vec4], [NodeValueType.Number], (context, data, inputs, outputs) => {", + " outputs[0] = inputs[0].dot(inputs[1]);", + "});", + "NodeTypes['dot'] = dot;", + "const cross = new NodeType('cross');", + "cross.addSignature([NodeValueType.Vec3, NodeValueType.Vec3], [NodeValueType.Vec3], (context, data, inputs, outputs) => {", + " outputs[0].crossVectors(inputs[0], inputs[1]);", + "});", + "NodeTypes['cross'] = cross;", + "const length = new NodeType('length');", + "length.addSignature([NodeValueType.Vec2], [NodeValueType.Number], (context, data, inputs, outputs) => {", + " outputs[0] = inputs[0].length();", + "});", + "length.addSignature([NodeValueType.Vec3], [NodeValueType.Number], (context, data, inputs, outputs) => {", + " outputs[0] = inputs[0].length();", + "});", + "length.addSignature([NodeValueType.Vec4], [NodeValueType.Number], (context, data, inputs, outputs) => {", + " outputs[0] = inputs[0].length();", + "});", + "NodeTypes['length'] = length;", + "const lengthSq = new NodeType('lengthSq');", + "lengthSq.addSignature([NodeValueType.Vec2], [NodeValueType.Number], (context, data, inputs, outputs) => {", + " outputs[0] = inputs[0].lengthSq();", + "});", + "lengthSq.addSignature([NodeValueType.Vec3], [NodeValueType.Number], (context, data, inputs, outputs) => {", + " outputs[0] = inputs[0].lengthSq();", + "});", + "lengthSq.addSignature([NodeValueType.Vec4], [NodeValueType.Number], (context, data, inputs, outputs) => {", + " outputs[0] = inputs[0].lengthSq();", + "});", + "NodeTypes['lengthSq'] = lengthSq;", + "const normalize = new NodeType('normalize');", + "normalize.addSignature([NodeValueType.Vec2], [NodeValueType.Vec2], (context, data, inputs, outputs) => {", + " outputs[0].copy(inputs[0]).normalize();", + "});", + "normalize.addSignature([NodeValueType.Vec3], [NodeValueType.Vec3], (context, data, inputs, outputs) => {", + " outputs[0].copy(inputs[0]).normalize();", + "});", + "normalize.addSignature([NodeValueType.Vec4], [NodeValueType.Vec4], (context, data, inputs, outputs) => {", + " outputs[0].copy(inputs[0]).normalize();", + "});", + "NodeTypes['normalize'] = normalize;", + "const distance = new NodeType('distance');", + "distance.addSignature([NodeValueType.Vec2, NodeValueType.Vec2], [NodeValueType.Number], (context, data, inputs, outputs) => {", + " outputs[0] = inputs[0].distanceTo(inputs[1]);", + "});", + "distance.addSignature([NodeValueType.Vec3, NodeValueType.Vec3], [NodeValueType.Number], (context, data, inputs, outputs) => {", + " outputs[0] = inputs[0].distanceTo(inputs[1]);", + "});", + "NodeTypes['distance'] = distance;", + "const andNode = new NodeType('and');", + "andNode.addSignature([NodeValueType.Boolean, NodeValueType.Boolean], [NodeValueType.Boolean], (context, data, inputs, outputs) => {", + " outputs[0] = inputs[0] && inputs[1];", + "});", + "NodeTypes['and'] = andNode;", + "const orNode = new NodeType('or');", + "orNode.addSignature([NodeValueType.Boolean, NodeValueType.Boolean], [NodeValueType.Boolean], (context, data, inputs, outputs) => {", + " outputs[0] = inputs[0] || inputs[1];", + "});", + "NodeTypes['or'] = orNode;", + "const notNode = new NodeType('not');", + "notNode.addSignature([NodeValueType.Boolean], [NodeValueType.Boolean], (context, data, inputs, outputs) => {", + " outputs[0] = !inputs[0];", + "});", + "NodeTypes['not'] = notNode;", + "const equalNode = new NodeType('equal');", + "equalNode.addSignature([NodeValueType.Number, NodeValueType.Number], [NodeValueType.Boolean], (context, data, inputs, outputs) => {", + " outputs[0] = inputs[0] === inputs[1];", + "});", + "equalNode.addSignature([NodeValueType.Vec2, NodeValueType.Vec2], [NodeValueType.Boolean], (context, data, inputs, outputs) => {", + " outputs[0] = inputs[0].equals(inputs[1]);", + "});", + "equalNode.addSignature([NodeValueType.Vec3, NodeValueType.Vec3], [NodeValueType.Boolean], (context, data, inputs, outputs) => {", + " outputs[0] = inputs[0].equals(inputs[1]);", + "});", + "equalNode.addSignature([NodeValueType.Vec4, NodeValueType.Vec4], [NodeValueType.Boolean], (context, data, inputs, outputs) => {", + " outputs[0] = inputs[0].equals(inputs[1]);", + "});", + "NodeTypes['equal'] = equalNode;", + "const lessThanNode = new NodeType('lessThan');", + "lessThanNode.addSignature([NodeValueType.Number, NodeValueType.Number], [NodeValueType.Boolean], (context, data, inputs, outputs) => {", + " outputs[0] = inputs[0] < inputs[1];", + "});", + "NodeTypes['lessThan'] = lessThanNode;", + "const greaterThanNode = new NodeType('greaterThan');", + "greaterThanNode.addSignature([NodeValueType.Number, NodeValueType.Number], [NodeValueType.Boolean], (context, data, inputs, outputs) => {", + " outputs[0] = inputs[0] > inputs[1];", + "});", + "NodeTypes['greaterThan'] = greaterThanNode;", + "const lessThanOrEqualNode = new NodeType('lessThanOrEqual');", + "lessThanOrEqualNode.addSignature([NodeValueType.Number, NodeValueType.Number], [NodeValueType.Boolean], (context, data, inputs, outputs) => {", + " outputs[0] = inputs[0] <= inputs[1];", + "});", + "NodeTypes['lessThanOrEqual'] = lessThanOrEqualNode;", + "const greaterThanOrEqualNode = new NodeType('greaterThanOrEqual');", + "greaterThanOrEqualNode.addSignature([NodeValueType.Number, NodeValueType.Number], [NodeValueType.Boolean], (context, data, inputs, outputs) => {", + " outputs[0] = inputs[0] >= inputs[1];", + "});", + "NodeTypes['greaterThanOrEqual'] = greaterThanOrEqualNode;", + "const ifNode = new NodeType('if');", + "ifNode.addSignature([NodeValueType.Boolean, NodeValueType.AnyType, NodeValueType.AnyType], [NodeValueType.AnyType], (context, data, inputs, outputs) => {", + " outputs[0] = inputs[0] ? inputs[1] : inputs[2];", + "});", + "NodeTypes['if'] = ifNode;", + "const numberNode = new NodeType('number');", + "numberNode.addSignature([], [NodeValueType.Number], (context, data, inputs, outputs) => {", + " outputs[0] = data.value;", + "});", + "NodeTypes['number'] = numberNode;", + "const vec2Node = new NodeType('vec2');", + "vec2Node.addSignature([NodeValueType.Number, NodeValueType.Number], [NodeValueType.Vec2], (context, data, inputs, outputs) => {", + " outputs[0].x = inputs[0];", + " outputs[0].y = inputs[1];", + "});", + "NodeTypes['vec2'] = vec2Node;", + "const vec3Node = new NodeType('vec3');", + "vec3Node.addSignature([NodeValueType.Number, NodeValueType.Number, NodeValueType.Number], [NodeValueType.Vec3], (context, data, inputs, outputs) => {", + " outputs[0].x = inputs[0];", + " outputs[0].y = inputs[1];", + " outputs[0].z = inputs[2];", + "});", + "NodeTypes['vec3'] = vec3Node;", + "const vec4Node = new NodeType('vec4');", + "vec4Node.addSignature([NodeValueType.Number, NodeValueType.Number, NodeValueType.Number, NodeValueType.Number], [NodeValueType.Vec4], (context, data, inputs, outputs) => {", + " outputs[0].x = inputs[0];", + " outputs[0].y = inputs[1];", + " outputs[0].z = inputs[2];", + " outputs[0].w = inputs[3];", + "});", + "NodeTypes['vec4'] = vec4Node;", + "const splitVec2Node = new NodeType('splitVec2');", + "splitVec2Node.addSignature([NodeValueType.Vec2], [NodeValueType.Number, NodeValueType.Number], (context, data, inputs, outputs) => {", + " outputs[0] = inputs[0].x;", + " outputs[1] = inputs[0].y;", + "});", + "NodeTypes['splitVec2'] = splitVec2Node;", + "const splitVec3Node = new NodeType('splitVec3');", + "splitVec3Node.addSignature([NodeValueType.Vec3], [NodeValueType.Number, NodeValueType.Number, NodeValueType.Number], (context, data, inputs, outputs) => {", + " outputs[0] = inputs[0].x;", + " outputs[1] = inputs[0].y;", + " outputs[2] = inputs[0].z;", + "});", + "NodeTypes['splitVec3'] = splitVec3Node;", + "const splitVec4Node = new NodeType('splitVec4');", + "splitVec4Node.addSignature([NodeValueType.Vec4], [NodeValueType.Number, NodeValueType.Number, NodeValueType.Number, NodeValueType.Number], (context, data, inputs, outputs) => {", + " outputs[0] = inputs[0].x;", + " outputs[1] = inputs[0].y;", + " outputs[2] = inputs[0].z;", + " outputs[3] = inputs[0].w;", + "});", + "NodeTypes['splitVec4'] = splitVec4Node;", + "const boolNode = new NodeType('bool');", + "boolNode.addSignature([], [NodeValueType.Boolean], (context, data, inputs, outputs) => {", + " outputs[0] = data.value;", + "});", + "NodeTypes['bool'] = boolNode;", + "const particlePropertyNode = new NodeType('particleProperty');", + "particlePropertyNode.addSignature([NodeValueType.NullableAnyType], [NodeValueType.NullableAnyType], (context, data, inputs, outputs) => {", + " if (inputs[0] !== undefined) {", + " if (typeof inputs[0] === 'object') {", + " context.particle[data.property].copy(inputs[0]);", + " }", + " else {", + " context.particle[data.property] = inputs[0];", + " }", + " }", + " if (context.particle[data.property] !== undefined) {", + " if (typeof outputs[0] === 'object') {", + " outputs[0].copy(context.particle[data.property]);", + " }", + " else {", + " outputs[0] = context.particle[data.property];", + " }", + " }", + "});", + "NodeTypes['particleProperty'] = particlePropertyNode;", + "const emitNode = new NodeType('emit');", + "emitNode.addSignature([NodeValueType.EventStream], [], (context, data, inputs, outputs) => {", + " const arr = inputs[0];", + " for (let i = 0; i < arr.length; i++) {", + " context.signal(i, arr[i]);", + " }", + "});", + "NodeTypes['emit'] = emitNode;", + "const graphPropertyNode = new NodeType('graphProperty');", + "graphPropertyNode.addSignature([NodeValueType.NullableAnyType], [NodeValueType.NullableAnyType], (context, data, inputs, outputs) => {", + " if (inputs[0] !== undefined) {", + " if (typeof inputs[0] === 'object') {", + " context.graph[data.property].copy(inputs[0]);", + " }", + " else {", + " context.graph[data.property] = inputs[0];", + " }", + " }", + " if (context.graph[data.property] !== undefined) {", + " if (typeof outputs[0] === 'object') {", + " outputs[0].copy(context.graph[data.property]);", + " }", + " else {", + " outputs[0] = context.graph[data.property];", + " }", + " }", + "});", + "NodeTypes['graphProperty'] = graphPropertyNode;", + "const startEventNode = new NodeType('startEvent');", + "startEventNode.addSignature([], [NodeValueType.EventStream], (context, data, inputs, outputs) => {", + " outputs[0] = [{ type: 'start' }];", + "});", + "NodeTypes['startEvent'] = startEventNode;", + "const repeaterNode = new NodeType('repeater');", + "repeaterNode.addSignature([NodeValueType.EventStream, NodeValueType.Number], [NodeValueType.EventStream], (context, data, inputs, outputs) => {", + " const arr = inputs[0];", + " const count = inputs[1];", + " const result = [];", + " for (let j = 0; j < arr.length; j++) {", + " for (let i = 0; i < count; i++) {", + " result.push(arr[j]);", + " }", + " }", + " outputs[0] = result;", + "});", + "NodeTypes['repeater'] = repeaterNode;", + "const timeNode = new NodeType('time');", + "timeNode.addSignature([], [NodeValueType.Number], (context, data, inputs, outputs) => {", + " outputs[0] = context.emissionState.time;", + "});", + "NodeTypes['time'] = timeNode;", + "const deltaNode = new NodeType('delta');", + "deltaNode.addSignature([], [NodeValueType.Number], (context, data, inputs, outputs) => {", + " outputs[0] = context.delta;", + "});", + "NodeTypes['delta'] = deltaNode;", + "const outputNode = new NodeType('output');", + "outputNode.addSignature([NodeValueType.Number], [NodeValueType.Number], (context, data, inputs, outputs) => {", + " outputs[0] = inputs[0];", + "});", + "outputNode.addSignature([NodeValueType.Vec2], [NodeValueType.Vec2], (context, data, inputs, outputs) => {", + " outputs[0] = inputs[0];", + "});", + "outputNode.addSignature([NodeValueType.Vec3], [NodeValueType.Vec3], (context, data, inputs, outputs) => {", + " outputs[0] = inputs[0];", + "});", + "outputNode.addSignature([NodeValueType.Vec4], [NodeValueType.Vec4], (context, data, inputs, outputs) => {", + " outputs[0] = inputs[0];", + "});", + "outputNode.addSignature([NodeValueType.Boolean], [NodeValueType.Boolean], (context, data, inputs, outputs) => {", + " outputs[0] = inputs[0];", + "});", + "NodeTypes['output'] = outputNode;", + "const lerpNode = new NodeType('lerp');", + "lerpNode.addSignature([NodeValueType.Number, NodeValueType.Number, NodeValueType.Number], [NodeValueType.Number], (context, data, inputs, outputs) => {", + " outputs[0] =", + " inputs[0] * (1 - inputs[2]) + inputs[1] * inputs[2];", + "});", + "lerpNode.addSignature([NodeValueType.Vec2, NodeValueType.Vec2, NodeValueType.Number], [NodeValueType.Vec2], (context, data, inputs, outputs) => {", + " outputs[0].lerpVectors(inputs[0], inputs[1], inputs[2]);", + "});", + "lerpNode.addSignature([NodeValueType.Vec3, NodeValueType.Vec3, NodeValueType.Number], [NodeValueType.Vec3], (context, data, inputs, outputs) => {", + " outputs[0].lerpVectors(inputs[0], inputs[1], inputs[2]);", + "});", + "lerpNode.addSignature([NodeValueType.Vec4, NodeValueType.Vec4, NodeValueType.Number], [NodeValueType.Vec4], (context, data, inputs, outputs) => {", + " outputs[0].lerpVectors(inputs[0], inputs[1], inputs[2]);", + "});", + "NodeTypes['lerp'] = lerpNode;", + "const normalD = (x) => {", + " return (1 / Math.sqrt(2 * Math.PI)) * Math.exp(x * x * -0.5);", + "};", + "const normalDistributionNode = new NodeType('normDistrib');", + "normalDistributionNode.addSignature([NodeValueType.Number], [NodeValueType.Number], (context, data, inputs, outputs) => {", + " outputs[0] = normalD(inputs[0]);", + "});", + "NodeTypes['normDistrib'] = normalDistributionNode;", + "const normcdfNode = new NodeType('normcdf');", + "normcdfNode.addSignature([NodeValueType.Number], [NodeValueType.Number], (context, data, inputs, outputs) => {", + " let x = inputs[0];", + " const a1 = 0.254829592;", + " const a2 = -0.284496736;", + " const a3 = 1.421413741;", + " const a4 = -1.453152027;", + " const a5 = 1.061405429;", + " const p = 0.3275911;", + " let sign = 1;", + " if (x < 0)", + " sign = -1;", + " x = Math.abs(x) / Math.sqrt(2.0);", + " const t = 1.0 / (1.0 + p * x);", + " const y = 1.0 - ((((a5 * t + a4) * t + a3) * t + a2) * t + a1) * t * Math.exp(-x * x);", + " outputs[0] = 0.5 * (1.0 + sign * y);", + "});", + "NodeTypes['normcdf'] = normcdfNode;", + "const normcdfInvNode = new NodeType('normcdfInv');", + "const rationalApproximation = (t) => {", + " const c = [2.515517, 0.802853, 0.010328];", + " const d = [1.432788, 0.189269, 0.001308];", + " return t - ((c[2] * t + c[1]) * t + c[0]) / (((d[2] * t + d[1]) * t + d[0]) * t + 1.0);", + "};", + "const normcdfInv = (p) => {", + " if (p < 0.5) {", + " return -rationalApproximation(Math.sqrt(-2.0 * Math.log(p)));", + " }", + " else {", + " return rationalApproximation(Math.sqrt(-2.0 * Math.log(1 - p)));", + " }", + "};", + "normcdfInvNode.addSignature([NodeValueType.Number], [NodeValueType.Number], (context, data, inputs, outputs) => {", + " outputs[0] = normcdfInv(inputs[0]);", + "});", + "NodeTypes['normcdfInv'] = normcdfInvNode;", + "const clampNode = new NodeType('clamp');", + "clampNode.addSignature([NodeValueType.Number, NodeValueType.Number, NodeValueType.Number], [NodeValueType.Number], (context, data, inputs, outputs) => {", + " outputs[0] = Math.max(Math.min(inputs[0], inputs[2]), inputs[1]);", + "});", + "NodeTypes['clamp'] = clampNode;", + "const smoothstepNode = new NodeType('smoothstep');", + "smoothstepNode.addSignature([NodeValueType.Number, NodeValueType.Number, NodeValueType.Number], [NodeValueType.Number], (context, data, inputs, outputs) => {", + " const x = Math.max(Math.min(inputs[0], inputs[2]), inputs[1]);", + " outputs[0] = x * x * (3 - 2 * x);", + "});", + "NodeTypes['smoothstep'] = smoothstepNode;", + "const randomNode = new NodeType('random');", + "randomNode.addSignature([NodeValueType.Number, NodeValueType.Number], [NodeValueType.Number], (context, data, inputs, outputs) => {", + " outputs[0] = Math.random() * (inputs[1] - inputs[0]) + inputs[0];", + "});", + "randomNode.addSignature([NodeValueType.Vec2, NodeValueType.Vec2], [NodeValueType.Vec2], (context, data, inputs, outputs) => {", + " let random = Math.random();", + " outputs[0].lerpVectors(inputs[0], inputs[1], random);", + "});", + "randomNode.addSignature([NodeValueType.Vec3, NodeValueType.Vec3], [NodeValueType.Vec3], (context, data, inputs, outputs) => {", + " let random = Math.random();", + " outputs[0].lerpVectors(inputs[0], inputs[1], random);", + "});", + "randomNode.addSignature([NodeValueType.Vec4, NodeValueType.Vec4], [NodeValueType.Vec4], (context, data, inputs, outputs) => {", + " let random = Math.random();", + " outputs[0].lerpVectors(inputs[0], inputs[1], random);", + "});", + "NodeTypes['random'] = randomNode;", + "const vrandNode = new NodeType('vrand');", + "vrandNode.addSignature([], [NodeValueType.Vec2], (context, data, inputs, outputs) => {", + " let x = normcdfInv(Math.random());", + " let y = normcdfInv(Math.random());", + " const mag = Math.sqrt(x * x + y * y);", + " outputs[0].set(x / mag, y / mag);", + "});", + "vrandNode.addSignature([], [NodeValueType.Vec3], (context, data, inputs, outputs) => {", + " let x = normcdfInv(Math.random());", + " let y = normcdfInv(Math.random());", + " let z = normcdfInv(Math.random());", + " const mag = Math.sqrt(x * x + y * y + z * z);", + " outputs[0].set(x / mag, y / mag, z / mag);", + "});", + "vrandNode.addSignature([], [NodeValueType.Vec4], (context, data, inputs, outputs) => {", + " let x = normcdfInv(Math.random());", + " let y = normcdfInv(Math.random());", + " let z = normcdfInv(Math.random());", + " let w = normcdfInv(Math.random());", + " const mag = Math.sqrt(x * x + y * y + z * z + w * w);", + " outputs[0].set(x / mag, y / mag, z / mag, w / mag);", + "});", + "NodeTypes['vrand'] = vrandNode;", + "const bsdfNode = new NodeType('bsdf');", + "bsdfNode.addSignature([NodeValueType.Vec3, NodeValueType.Vec3, NodeValueType.Vec3, NodeValueType.Number], [], (context, data, inputs, outputs) => { });", + "NodeTypes['bsdf'] = bsdfNode;", + "const OutputNodeTypeNames = new Set(['output', 'particleProperty', 'graphProperty', 'emit']);", + "", + "class NodeGraph {", + " constructor(name) {", + " this.inputNodes = [];", + " this.outputNodes = [];", + " this.allNodes = new Map();", + " this.wires = [];", + " this.compiled = false;", + " this.nodesInOrder = [];", + " this.version = '1.0';", + " this.uuid = THREE.MathUtils.generateUUID();", + " this.name = name;", + " this.revision = 0;", + " }", + " addWire(wire) {", + " this.wires.push(wire);", + " this.revision++;", + " }", + " addNode(node) {", + " this.allNodes.set(node.id, node);", + " if (node.type === NodeTypes['input']) {", + " this.inputNodes.push(node);", + " }", + " else if (OutputNodeTypeNames.has(node.type.name)) {", + " this.outputNodes.push(node);", + " }", + " this.revision++;", + " }", + " getNode(id) {", + " return this.allNodes.get(id);", + " }", + " deleteNode(node) {", + " this.allNodes.delete(node.id);", + " this.revision++;", + " }", + " deleteWire(wire) {", + " let index = wire.input.outputs[wire.inputIndex].indexOf(wire);", + " if (index !== -1) {", + " wire.input.outputs[wire.inputIndex].splice(index, 1);", + " }", + " wire.output.inputs[wire.outputIndex] = undefined;", + " index = this.wires.indexOf(wire);", + " if (index != -1) {", + " this.wires[index] = this.wires[this.wires.length - 1];", + " this.wires.pop();", + " }", + " this.revision++;", + " }", + " toJSON() {", + " throw new Error('not implemented');", + " }", + " clone() {", + " throw new Error('not implemented');", + " }", + "}", + "", + "new THREE.Vector3(0, 0, 1);", + "const tempQ = new THREE.Quaternion();", + "const tempV = new THREE.Vector3();", + "const tempV2 = new THREE.Vector3();", + "const PREWARM_FPS = 60;", + "const DEFAULT_GEOMETRY = new THREE.PlaneGeometry(1, 1, 1, 1);", + "class NodeVFX {", + " set time(time) {", + " this.emissionState.time = time;", + " }", + " get time() {", + " return this.emissionState.time;", + " }", + " get layers() {", + " return this.rendererSettings.layers;", + " }", + " get texture() {", + " return this.rendererSettings.material.map;", + " }", + " set texture(texture) {", + " this.rendererSettings.material.map = texture;", + " this.neededToUpdateRender = true;", + " }", + " get material() {", + " return this.rendererSettings.material;", + " }", + " set material(material) {", + " this.rendererSettings.material = material;", + " this.neededToUpdateRender = true;", + " }", + " get instancingGeometry() {", + " return this.rendererSettings.instancingGeometry;", + " }", + " set instancingGeometry(geometry) {", + " this.restart();", + " this.particles.length = 0;", + " this.rendererSettings.instancingGeometry = geometry;", + " this.neededToUpdateRender = true;", + " }", + " get renderMode() {", + " return this.rendererSettings.renderMode;", + " }", + " set renderMode(renderMode) {", + " if ((this.rendererSettings.renderMode != RenderMode.Trail && renderMode === RenderMode.Trail) ||", + " (this.rendererSettings.renderMode == RenderMode.Trail && renderMode !== RenderMode.Trail)) {", + " this.restart();", + " this.particles.length = 0;", + " }", + " if (this.rendererSettings.renderMode !== renderMode) {", + " switch (renderMode) {", + " case RenderMode.Trail:", + " this.rendererEmitterSettings = {", + " startLength: 30,", + " followLocalOrigin: false,", + " };", + " break;", + " case RenderMode.Mesh:", + " this.rendererEmitterSettings = {", + " geometry: new THREE.PlaneGeometry(1, 1),", + " };", + " break;", + " case RenderMode.BillBoard:", + " case RenderMode.VerticalBillBoard:", + " case RenderMode.HorizontalBillBoard:", + " case RenderMode.StretchedBillBoard:", + " this.rendererEmitterSettings = {};", + " break;", + " }", + " }", + " this.rendererSettings.renderMode = renderMode;", + " this.neededToUpdateRender = true;", + " }", + " get renderOrder() {", + " return this.rendererSettings.renderOrder;", + " }", + " set renderOrder(renderOrder) {", + " this.rendererSettings.renderOrder = renderOrder;", + " this.neededToUpdateRender = true;", + " }", + " get blending() {", + " return this.rendererSettings.material.blending;", + " }", + " set blending(blending) {", + " this.rendererSettings.material.blending = blending;", + " this.neededToUpdateRender = true;", + " }", + " constructor(parameters) {", + " var _a, _b, _c, _d, _e, _f, _g;", + " this.temp = new THREE.Vector3();", + " this.travelDistance = 0;", + " this.normalMatrix = new THREE.Matrix3();", + " this.speedFactor = 0;", + " this.autoDestroy = parameters.autoDestroy === undefined ? false : parameters.autoDestroy;", + " this.duration = (_a = parameters.duration) !== null && _a !== void 0 ? _a : 1;", + " this.looping = parameters.looping === undefined ? true : parameters.looping;", + " this.prewarm = parameters.prewarm === undefined ? false : parameters.prewarm;", + " this.worldSpace = (_b = parameters.worldSpace) !== null && _b !== void 0 ? _b : false;", + " this.rendererEmitterSettings = (_c = parameters.rendererEmitterSettings) !== null && _c !== void 0 ? _c : {};", + " this.emissionGraph = parameters.emissionGraph;", + " this.updateGraph = parameters.updateGraph;", + " this.interpreter = new Interpreter();", + " this.rendererSettings = {", + " instancingGeometry: (_d = parameters.instancingGeometry) !== null && _d !== void 0 ? _d : DEFAULT_GEOMETRY,", + " renderMode: (_e = parameters.renderMode) !== null && _e !== void 0 ? _e : RenderMode.BillBoard,", + " renderOrder: (_f = parameters.renderOrder) !== null && _f !== void 0 ? _f : 0,", + " material: parameters.material,", + " layers: (_g = parameters.layers) !== null && _g !== void 0 ? _g : new THREE.Layers(),", + " uTileCount: 1,", + " vTileCount: 1,", + " };", + " this.neededToUpdateRender = true;", + " this.particles = new Array();", + " this.emitter = new ParticleEmitter(this);", + " this.paused = false;", + " this.particleNum = 0;", + " this.emissionState = {", + " time: 0,", + " };", + " this.emitEnded = false;", + " this.markForDestroy = false;", + " this.prewarmed = false;", + " }", + " pause() {", + " this.paused = true;", + " }", + " play() {", + " this.paused = false;", + " }", + " spawn(emissionState, matrix) {", + " tempQ.setFromRotationMatrix(matrix);", + " const translation = tempV;", + " const quaternion = tempQ;", + " const scale = tempV2;", + " matrix.decompose(translation, quaternion, scale);", + " this.particleNum++;", + " while (this.particles.length < this.particleNum) {", + " this.particles.push(new NodeParticle());", + " }", + " const particle = this.particles[this.particleNum - 1];", + " particle.reset();", + " this.interpreter.run(this.updateGraph, { particle: particle, emissionState: this.emissionState });", + " if (this.rendererSettings.renderMode === RenderMode.Trail &&", + " this.rendererEmitterSettings.followLocalOrigin) {", + " const trail = particle;", + " trail.localPosition = new THREE.Vector3().copy(trail.position);", + " }", + " if (this.worldSpace) {", + " particle.position.applyMatrix4(matrix);", + " particle.size *= (Math.abs(scale.x) + Math.abs(scale.y) + Math.abs(scale.z)) / 3;", + " particle.velocity.multiply(scale).applyMatrix3(this.normalMatrix);", + " if (particle.rotation && particle.rotation instanceof THREE.Quaternion) {", + " particle.rotation.multiplyQuaternions(tempQ, particle.rotation);", + " }", + " }", + " }", + " endEmit() {", + " this.emitEnded = true;", + " if (this.autoDestroy) {", + " this.markForDestroy = true;", + " }", + " }", + " dispose() {", + " if (this._renderer)", + " this._renderer.deleteSystem(this);", + " this.emitter.dispose();", + " if (this.emitter.parent)", + " this.emitter.parent.remove(this.emitter);", + " }", + " restart() {", + " this.paused = false;", + " this.particleNum = 0;", + " this.emissionState.time = 0;", + " this.emitEnded = false;", + " this.markForDestroy = false;", + " this.prewarmed = false;", + " }", + " update(delta) {", + " if (this.paused)", + " return;", + " let currentParent = this.emitter;", + " while (currentParent.parent) {", + " currentParent = currentParent.parent;", + " }", + " if (currentParent.type !== 'Scene') {", + " this.dispose();", + " return;", + " }", + " if (this.emitEnded && this.particleNum === 0) {", + " if (this.markForDestroy && this.emitter.parent)", + " this.dispose();", + " return;", + " }", + " if (this.looping && this.prewarm && !this.prewarmed) {", + " this.prewarmed = true;", + " for (let i = 0; i < this.duration * PREWARM_FPS; i++) {", + " this.update(1.0 / PREWARM_FPS);", + " }", + " }", + " if (delta > 0.1) {", + " delta = 0.1;", + " }", + " if (this.neededToUpdateRender) {", + " if (this._renderer)", + " this._renderer.updateSystem(this);", + " this.neededToUpdateRender = false;", + " }", + " this.emit(delta, this.emissionState, this.emitter.matrixWorld);", + " const context = { particle: undefined, emissionState: this.emissionState, delta };", + " for (let i = 0; i < this.particleNum; i++) {", + " context.particle = this.particles[i];", + " this.interpreter.run(this.updateGraph, context);", + " }", + " for (let i = 0; i < this.particleNum; i++) {", + " if (this.rendererEmitterSettings.followLocalOrigin &&", + " this.particles[i].localPosition) {", + " this.particles[i].position.copy(this.particles[i].localPosition);", + " if (this.particles[i].parentMatrix) {", + " this.particles[i].position.applyMatrix4(this.particles[i].parentMatrix);", + " }", + " else {", + " this.particles[i].position.applyMatrix4(this.emitter.matrixWorld);", + " }", + " }", + " else {", + " this.particles[i].position.addScaledVector(this.particles[i].velocity, delta);", + " }", + " this.particles[i].age += delta;", + " }", + " if (this.rendererSettings.renderMode === RenderMode.Trail) {", + " for (let i = 0; i < this.particleNum; i++) {", + " const particle = this.particles[i];", + " particle.update();", + " }", + " }", + " for (let i = 0; i < this.particleNum; i++) {", + " const particle = this.particles[i];", + " if (particle.died && (!(particle instanceof TrailParticle) || particle.previous.length === 0)) {", + " this.particles[i] = this.particles[this.particleNum - 1];", + " this.particles[this.particleNum - 1] = particle;", + " this.particleNum--;", + " i--;", + " }", + " }", + " }", + " emit(delta, emissionState, emitterMatrix) {", + " if (emissionState.time > this.duration) {", + " if (this.looping) {", + " emissionState.time -= this.duration;", + " }", + " else {", + " if (!this.emitEnded) {", + " this.endEmit();", + " }", + " }", + " }", + " this.normalMatrix.getNormalMatrix(emitterMatrix);", + " const context = {", + " signal: () => {", + " this.spawn(emissionState, emitterMatrix);", + " },", + " emissionState,", + " delta,", + " };", + " if (!this.emitEnded) {", + " this.interpreter.run(this.emissionGraph, context);", + " }", + " if (this.previousWorldPos === undefined)", + " this.previousWorldPos = new THREE.Vector3();", + " this.emitter.getWorldPosition(this.previousWorldPos);", + " emissionState.time += delta;", + " }", + " toJSON(meta, options = {}) {", + " return {};", + " }", + " getRendererSettings() {", + " return this.rendererSettings;", + " }", + " clone() {", + " return this;", + " }", + "}", + "", + "gdjs.__particleEmmiter3DExtension = {", + " ParticleEmitter3DRenderer,", + " ParticleEmitterAdapter,", + " ParticleEmitterHelper,", + "", + " ApplyCollision,", + " ApplyForce,", + " ApplySequences,", + " AxisAngleGenerator,", + " BatchedParticleRenderer,", + " BatchedRenderer,", + " BehaviorFromJSON,", + " BehaviorTypes,", + " Bezier,", + " ChangeEmitDirection,", + " ColorGeneratorFromJSON,", + " ColorOverLife,", + " ColorRange,", + " ConeEmitter,", + " ConstantColor,", + " ConstantValue,", + " DonutEmitter,", + " EmitSubParticleSystem,", + " EmitterFromJSON,", + " EmitterMode,", + " EmitterShapes,", + " EulerGenerator,", + " ForceOverLife,", + " FrameOverLife,", + " GeneratorFromJSON,", + " Gradient,", + " GraphNodeType,", + " GravityForce,", + " GridEmitter,", + " Interpreter,", + " IntervalValue,", + " MeshSurfaceEmitter,", + " Node,", + " NodeGraph,", + " NodeType,", + " NodeTypes,", + " NodeValueType,", + " Noise,", + " OrbitOverLife,", + " ParticleEmitter,", + " ParticleSystem,", + " PiecewiseBezier,", + " PiecewiseFunction,", + " Plugins,", + " PointEmitter,", + " QuarksLoader,", + " RandomColor,", + " RandomColorBetweenGradient,", + " RandomQuatGenerator,", + " RecordState,", + " RenderMode,", + " Rotation3DOverLife,", + " RotationGeneratorFromJSON,", + " RotationOverLife,", + " SequencerFromJSON,", + " SizeOverLife,", + " SpeedOverLife,", + " SphereEmitter,", + " SpriteBatch,", + " SpriteParticle,", + " SubParticleEmitMode,", + " TextureSequencer,", + " TrailBatch,", + " TrailParticle,", + " TurbulenceField,", + " VFXBatch,", + " ValueGeneratorFromJSON,", + " WidthOverLength,", + " Wire,", + " genDefaultForNodeValueType,", + " getPhysicsResolver,", + " loadPlugin,", + " setPhysicsResolver,", + " unloadPlugin,", + "}" + ], + "parameterObjects": "", + "useStrict": true, + "eventsSheetExpanded": true + } + ], + "parameters": [], + "objectGroups": [] + }, + { + "fullName": "", + "functionType": "Action", + "name": "onScenePreEvents", + "sentence": "", + "events": [ + { + "type": "BuiltinCommonInstructions::JsCode", + "inlineCode": [ + "\r", + "// See doStepPostEvents\r", + "runtimeScene.__particleEmmiter3DExtension = runtimeScene.__particleEmmiter3DExtension || {};\r", + "runtimeScene.__particleEmmiter3DExtension.emittersStepped = 0;" + ], + "parameterObjects": "", + "useStrict": true, + "eventsSheetExpanded": true + } + ], + "parameters": [], + "objectGroups": [] + } + ], + "eventsFunctionsFolderStructure": { + "folderName": "__ROOT", + "children": [ + { + "functionName": "DefineHelperClasses" + }, + { + "functionName": "onScenePreEvents" + } + ] + }, + "eventsBasedBehaviors": [], + "eventsBasedObjects": [ + { + "areaMaxX": 64, + "areaMaxY": 64, + "areaMaxZ": 64, + "areaMinX": 0, + "areaMinY": 0, + "areaMinZ": 0, + "assetStoreTag": "3d particles", + "defaultName": "ParticleEmitter", + "description": "Display a large number of particles to create visual effects.", + "fullName": "3D particle emitter", + "helpPath": "", + "iconUrl": "", + "is3D": true, + "isUsingLegacyInstancesRenderer": true, + "name": "ParticleEmitter3D", + "previewIconUrl": "", + "objects": [ + { + "adaptCollisionMaskAutomatically": true, + "assetStoreId": "", + "name": "Particle", + "type": "Sprite", + "updateIfNotVisible": false, + "variables": [], + "effects": [], + "behaviors": [], + "animations": [ + { + "name": "Image", + "useMultipleDirections": false, + "directions": [ + { + "looping": false, + "timeBetweenFrames": 0.08, + "sprites": [] + } + ] + } + ] + } + ], + "objectsFolderStructure": { + "folderName": "__ROOT", + "children": [ + { + "objectName": "Particle" + } + ] + }, + "objectsGroups": [], + "layers": [ + { + "ambientLightColorB": 200, + "ambientLightColorG": 200, + "ambientLightColorR": 200, + "camera2DPlaneMaxDrawingDistance": 5000, + "camera3DFarPlaneDistance": 10000, + "camera3DFieldOfView": 45, + "camera3DNearPlaneDistance": 3, + "cameraType": "", + "followBaseLayerCamera": false, + "isLightingLayer": false, + "isLocked": false, + "name": "", + "renderingType": "", + "visibility": true, + "cameras": [ + { + "defaultSize": true, + "defaultViewport": true, + "height": 0, + "viewportBottom": 1, + "viewportLeft": 0, + "viewportRight": 1, + "viewportTop": 0, + "width": 0 + } + ], + "effects": [] + } + ], + "instances": [], + "editionSettings": [], + "eventsFunctions": [ + { + "fullName": "", + "functionType": "Action", + "name": "onCreated", + "sentence": "", + "events": [ + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [], + "actions": [ + { + "type": { + "value": "ParticleEmitter3D::DefineHelperClasses" + }, + "parameters": [ + "", + "" + ] + } + ] + }, + { + "type": "BuiltinCommonInstructions::JsCode", + "inlineCode": [ + "const {", + " ParticleEmitterAdapter,", + " ParticleEmitter3DRenderer,", + " ParticleEmitterHelper,", + "", + " ParticleSystem,", + " IntervalValue,", + " ConstantValue,", + " ConstantColor,", + " ColorOverLife,", + " SizeOverLife,", + " ApplyForce,", + " WidthOverLength,", + " Gradient,", + " PiecewiseBezier,", + " Bezier,", + " PointEmitter,", + " ConeEmitter,", + " RenderMode", + "} = gdjs.__particleEmmiter3DExtension;", + "", + "/** @type {gdjs.CustomRuntimeObject} */", + "const object = objects[0];", + "// Force the size of the object because it doesn't contain any instance.", + "object._innerArea = {", + " min: [-16, -16, -16],", + " max: [16, 16, 16],", + " };", + "const hitbox = new gdjs.Polygon();", + "hitbox.vertices = [[-16, -16], [16, -16], [16, 16], [-16, 16]];", + "object._untransformedHitBoxes = [hitbox];", + "object._unrotatedAABB.min[0] = -16;", + "object._unrotatedAABB.min[1] = -16;", + "object._unrotatedAABB.max[0] = 16;", + "object._unrotatedAABB.max[1] = 16;", + "object._updateUntransformedHitBoxes = () => {};", + "", + "// Here runtimeScene is the gdjs.CustomRuntimeObjectInstanceContainer inside the custom object.", + "const gameScene = object.getRuntimeScene();", + "const game = runtimeScene.getGame();", + "", + "const texture = game.getImageManager().getThreeTexture('');", + "", + "// Set the blending here because changes are not applied after the emitter creation.", + "const blendingString = object._getBlending();", + "const blending =", + " blendingString === \"Additive\" ? THREE.AdditiveBlending :", + " blendingString === \"Normal\" ? THREE.NormalBlending :", + " blendingString === \"Subtractive\" ? THREE.SubtractiveBlending :", + " blendingString === \"Multiply\" ? THREE.MultiplyBlending :", + " blendingString === \"None\" ? THREE.NoBlending :", + " THREE.AdditiveBlending;", + "", + "", + "// Build a configuration with the right kind of objects.", + "// These values are not important as they are overrided by the object properties values.", + "const muzzle = {", + " duration: 10,", + " looping: false,", + " startLife: new IntervalValue(1, 2),", + " startSpeed: new IntervalValue(100, 200),", + " startSize: new IntervalValue(20, 50),", + " startColor: new ConstantColor(new THREE.Vector4(1, 1, 1, 1)),", + " worldSpace: true,", + "", + " maxParticle: 5,", + " emissionOverTime: new ConstantValue(50),", + " emissionBursts: [{", + " time: 0,", + " count: new ConstantValue(1),", + " cycle: 1,", + " interval: 0.01,", + " probability: 1,", + " }],", + "", + " shape: new PointEmitter(),", + " material: new THREE.MeshBasicMaterial({", + " map: texture,", + " blending: blending,", + " transparent: true,", + " side: THREE.DoubleSide", + " }),", + " startTileIndex: 0,", + " uTileCount: 1,", + " vTileCount: 1,", + " renderOrder: 2,", + " renderMode: RenderMode.BillBoard", + "};", + "const particleSystem = new ParticleSystem(muzzle);", + "", + "const colorOverLife = new ColorOverLife(", + " new Gradient(", + " [", + " [new THREE.Vector3(1, 0, 0), 0],", + " [new THREE.Vector3(1, 0, 0), 1],", + " ],", + " [", + " [1, 0],", + " [1, 1],", + " ]", + " ));", + "particleSystem.addBehavior(colorOverLife);", + "const sizeOverLife = new SizeOverLife(new PiecewiseBezier([[new Bezier(1, 2 / 3, 1 / 3, 0), 0]]));", + "particleSystem.addBehavior(sizeOverLife);", + "const applyForce = new ApplyForce(new THREE.Vector3(0, 0, -1), new ConstantValue(0));", + "particleSystem.addBehavior(applyForce);", + "const widthOverLength = new WidthOverLength(new PiecewiseBezier([[new Bezier(1, 2 / 3, 1 / 3, 0), 0]]));", + "particleSystem.addBehavior(widthOverLength);", + "", + "particleSystem.emitter.name = object.getName() + object.getNameId();", + "", + "object.__particleEmitterAdapter = new ParticleEmitterAdapter(particleSystem, colorOverLife, sizeOverLife, applyForce, widthOverLength);", + "object.__particleSystem = particleSystem;", + "", + "// This is a hack that may break in future releases.", + "// Replace the group that would hold children objects by the emmiter.", + "const layer = object.getInstanceContainer().getLayer(object.getLayer());", + "const group = object.getRenderer()._threeGroup;", + "layer.getRenderer().remove3DRendererObject(group);", + "particleSystem.emitter.position.copy(group.position);", + "particleSystem.emitter.rotation.order = 'ZYX';", + "particleSystem.emitter.rotation.copy(group.rotation);", + "", + "const particleEmitter3DRenderer = new ParticleEmitter3DRenderer(object, object._instanceContainer, object.getInstanceContainer());", + "object._renderer = particleEmitter3DRenderer;", + "if (game.isInGameEdition && game.isInGameEdition()) {", + " const particleEmitterHelper = new ParticleEmitterHelper(object);", + " particleEmitter3DRenderer._threeGroup = particleEmitterHelper;", + " layer.getRenderer().add3DRendererObject(particleEmitterHelper);", + "}", + "else {", + " particleEmitter3DRenderer._threeGroup = particleSystem.emitter;", + " layer.getRenderer().add3DRendererObject(particleSystem.emitter);", + "}", + "", + "particleSystem.emitter.updateMatrixWorld(true);", + "", + "", + "// See doStepPostEvents", + "gameScene.__particleEmmiter3DExtension = gameScene.__particleEmmiter3DExtension || {};", + "gameScene.__particleEmmiter3DExtension.emittersCount = gameScene.__particleEmmiter3DExtension.emittersCount || 0;", + "gameScene.__particleEmmiter3DExtension.emittersCount++;", + "", + "" + ], + "parameterObjects": "Object", + "useStrict": true, + "eventsSheetExpanded": true + }, + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [], + "actions": [ + { + "type": { + "value": "ParticleEmitter3D::ParticleEmitter3D::UpdateFromProperties" + }, + "parameters": [ + "Object", + "" + ] + }, + { + "type": { + "value": "ParticleEmitter3D::ParticleEmitter3D::SetRotationCenter" + }, + "parameters": [ + "Object", + "0", + "0", + "0" + ] + } + ] + } + ], + "parameters": [ + { + "description": "Object", + "name": "Object", + "supplementaryInformation": "ParticleEmitter3D::ParticleEmitter3D", + "type": "object" + } + ], + "objectGroups": [] + }, + { + "fullName": "", + "functionType": "Action", + "name": "onDestroy", + "sentence": "", + "events": [ + { + "type": "BuiltinCommonInstructions::JsCode", + "inlineCode": [ + "/** @type {gdjs.CustomRuntimeObject} */", + "const object = objects[0];", + "// Here runtimeScene is the gdjs.CustomRuntimeObjectInstanceContainer inside the custom object.", + "const gameScene = object.getRuntimeScene();", + "", + "object.__particleSystem.dispose();", + "", + "// See doStepPostEvents", + "gameScene.__particleEmmiter3DExtension.emittersCount--;", + "", + "if (gameScene.__particleEmmiter3DExtension.emittersCount === 0) {", + " // Update batch system now because doStepPostEvents won't be called.", + " gameScene.__particleEmmiter3DExtension.layerNames = gameScene.__particleEmmiter3DExtension.layerNames || [];", + " const layerNames = gameScene.__particleEmmiter3DExtension.layerNames;", + " gameScene.getAllLayerNames(layerNames);", + " for (const layerName of layerNames) {", + " const layer = gameScene.getLayer(layerName);", + " if (layer.__particleEmmiter3DExtension) {", + " layer.__particleEmmiter3DExtension.batchSystem.update(layer.getElapsedTime() / 1000);", + " }", + " }", + "}" + ], + "parameterObjects": "Object", + "useStrict": true, + "eventsSheetExpanded": true + } + ], + "parameters": [ + { + "description": "Object", + "name": "Object", + "supplementaryInformation": "ParticleEmitter3D::ParticleEmitter3D", + "type": "object" + } + ], + "objectGroups": [] + }, + { + "fullName": "", + "functionType": "Action", + "name": "onHotReloading", + "sentence": "", + "events": [ + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [], + "actions": [ + { + "type": { + "value": "ParticleEmitter3D::ParticleEmitter3D::UpdateFromProperties" + }, + "parameters": [ + "Object", + "" + ] + } + ] + } + ], + "parameters": [ + { + "description": "Object", + "name": "Object", + "supplementaryInformation": "ParticleEmitter3D::ParticleEmitter3D", + "type": "object" + } + ], + "objectGroups": [] + }, + { + "description": "Update from properties.", + "fullName": "Update from properties", + "functionType": "Action", + "name": "UpdateFromProperties", + "private": true, + "sentence": "Update from properties of _PARAM0_", + "events": [ + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [], + "actions": [ + { + "type": { + "value": "ParticleEmitter3D::ParticleEmitter3D::SetZ" + }, + "parameters": [ + "Object", + "=", + "Object.Z()", + "" + ] + }, + { + "type": { + "value": "ParticleEmitter3D::ParticleEmitter3D::SetRotationX" + }, + "parameters": [ + "Object", + "=", + "Object.PropertyRotationX()", + "" + ] + }, + { + "type": { + "value": "ParticleEmitter3D::ParticleEmitter3D::SetRotationY" + }, + "parameters": [ + "Object", + "=", + "Object.PropertyRotationY()", + "" + ] + }, + { + "type": { + "value": "ParticleEmitter3D::ParticleEmitter3D::SetRenderMode" + }, + "parameters": [ + "Object", + "=", + "RenderMode", + "" + ] + }, + { + "type": { + "value": "ParticleEmitter3D::ParticleEmitter3D::SetTrailStartLengthMin" + }, + "parameters": [ + "Object", + "=", + "TrailStartLengthMin", + "" + ] + }, + { + "type": { + "value": "ParticleEmitter3D::ParticleEmitter3D::SetTrailStartLengthMax" + }, + "parameters": [ + "Object", + "=", + "TrailStartLengthMax", + "" + ] + }, + { + "type": { + "value": "ParticleEmitter3D::ParticleEmitter3D::SetTrailEndWidthRatio" + }, + "parameters": [ + "Object", + "=", + "TrailEndWidthRatio", + "" + ] + }, + { + "type": { + "value": "ParticleEmitter3D::ParticleEmitter3D::SetStartColor" + }, + "parameters": [ + "Object", + "=", + "Object.StartColor()", + "" + ] + }, + { + "type": { + "value": "ParticleEmitter3D::ParticleEmitter3D::SetEndColor" + }, + "parameters": [ + "Object", + "=", + "Object.EndColor()", + "" + ] + }, + { + "type": { + "value": "ParticleEmitter3D::ParticleEmitter3D::SetStartOpacity" + }, + "parameters": [ + "Object", + "=", + "Object.StartOpacity()", + "" + ] + }, + { + "type": { + "value": "ParticleEmitter3D::ParticleEmitter3D::SetEndOpacity" + }, + "parameters": [ + "Object", + "=", + "Object.EndOpacity()", + "" + ] + }, + { + "type": { + "value": "ParticleEmitter3D::ParticleEmitter3D::SetFlow" + }, + "parameters": [ + "Object", + "=", + "Object.Flow()", + "" + ] + }, + { + "type": { + "value": "ParticleEmitter3D::ParticleEmitter3D::SetStartSizeMin" + }, + "parameters": [ + "Object", + "=", + "Object.StartSizeMin()", + "" + ] + }, + { + "type": { + "value": "ParticleEmitter3D::ParticleEmitter3D::SetStartSizeMax" + }, + "parameters": [ + "Object", + "=", + "Object.StartSizeMax()", + "" + ] + }, + { + "type": { + "value": "ParticleEmitter3D::ParticleEmitter3D::SetEndScale" + }, + "parameters": [ + "Object", + "=", + "Object.EndScale()", + "" + ] + }, + { + "type": { + "value": "ParticleEmitter3D::ParticleEmitter3D::SetStartSpeedMin" + }, + "parameters": [ + "Object", + "=", + "Object.StartSpeedMin()", + "" + ] + }, + { + "type": { + "value": "ParticleEmitter3D::ParticleEmitter3D::SetStartSpeedMax" + }, + "parameters": [ + "Object", + "=", + "Object.StartSpeedMax()", + "" + ] + }, + { + "type": { + "value": "ParticleEmitter3D::ParticleEmitter3D::SetLifespanMin" + }, + "parameters": [ + "Object", + "=", + "Object.LifespanMin()", + "" + ] + }, + { + "type": { + "value": "ParticleEmitter3D::ParticleEmitter3D::SetLifespanMax" + }, + "parameters": [ + "Object", + "=", + "Object.LifespanMax()", + "" + ] + }, + { + "type": { + "value": "ParticleEmitter3D::ParticleEmitter3D::SetDuration" + }, + "parameters": [ + "Object", + "=", + "Object.Duration()", + "" + ] + }, + { + "type": { + "value": "ParticleEmitter3D::ParticleEmitter3D::SetSpayConeAngle" + }, + "parameters": [ + "Object", + "=", + "Object.SpayConeAngle()", + "" + ] + }, + { + "type": { + "value": "ParticleEmitter3D::ParticleEmitter3D::SetGravity" + }, + "parameters": [ + "Object", + "=", + "Object.Gravity()", + "" + ] + }, + { + "type": { + "value": "ParticleEmitter3D::ParticleEmitter3D::SetGravityTop" + }, + "parameters": [ + "Object", + "=", + "Object.GravityTop()", + "" + ] + }, + { + "type": { + "value": "ParticleEmitter3D::ParticleEmitter3D::UpdateImage" + }, + "parameters": [ + "Object", + "" + ] + } + ], + "events": [ + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [ + { + "type": { + "inverted": true, + "value": "BooleanVariable" + }, + "parameters": [ + "IsTrailFollowingLocalOrigin", + "True", + "" + ] + } + ], + "actions": [ + { + "type": { + "value": "ParticleEmitter3D::ParticleEmitter3D::SetIsTrailFollowingLocalOrigin" + }, + "parameters": [ + "Object", + "=", + "TrailStartLengthMax" + ] + } + ] + }, + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [ + { + "type": { + "value": "BooleanVariable" + }, + "parameters": [ + "IsTrailFollowingLocalOrigin", + "True", + "" + ] + } + ], + "actions": [ + { + "type": { + "value": "ParticleEmitter3D::ParticleEmitter3D::SetIsTrailFollowingLocalOrigin" + }, + "parameters": [ + "Object", + "yes", + "TrailStartLengthMax" + ] + } + ] + }, + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [ + { + "type": { + "inverted": true, + "value": "BooleanVariable" + }, + "parameters": [ + "AreParticlesRelative", + "True", + "" + ] + } + ], + "actions": [ + { + "type": { + "value": "ParticleEmitter3D::ParticleEmitter3D::SetAreParticlesRelative" + }, + "parameters": [ + "Object", + "no", + "" + ] + } + ] + }, + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [ + { + "type": { + "value": "BooleanVariable" + }, + "parameters": [ + "AreParticlesRelative", + "True", + "" + ] + } + ], + "actions": [ + { + "type": { + "value": "ParticleEmitter3D::ParticleEmitter3D::SetAreParticlesRelative" + }, + "parameters": [ + "Object", + "yes", + "" + ] + } + ] + }, + { + "type": "BuiltinCommonInstructions::Comment", + "color": { + "b": 109, + "g": 230, + "r": 255, + "textB": 0, + "textG": 0, + "textR": 0 + }, + "comment": "TODO: Blending can't be changed." + }, + { + "disabled": true, + "type": "BuiltinCommonInstructions::Standard", + "conditions": [], + "actions": [ + { + "type": { + "value": "ParticleEmitter3D::ParticleEmitter3D::SetBlending" + }, + "parameters": [ + "Object", + "=", + "Object.Blending()", + "" + ] + } + ] + } + ] + }, + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [], + "actions": [ + { + "type": { + "value": "ParticleEmitter3D::ParticleEmitter3D::UpdateHelper" + }, + "parameters": [ + "Object", + "" + ] + } + ] + } + ], + "parameters": [ + { + "description": "Object", + "name": "Object", + "supplementaryInformation": "ParticleEmitter3D::ParticleEmitter3D", + "type": "object" + } + ], + "objectGroups": [] + }, + { + "fullName": "Update helper", + "functionType": "Action", + "name": "UpdateHelper", + "private": true, + "sentence": "Update graphical helper of _PARAM0_", + "events": [ + { + "type": "BuiltinCommonInstructions::JsCode", + "inlineCode": [ + "const game = runtimeScene.getGame();", + "if (game.isInGameEdition && game.isInGameEdition()) {", + " const particleEmitterHelper = objects[0].get3DRendererObject();", + " particleEmitterHelper.update();", + "}" + ], + "parameterObjects": "Object", + "useStrict": true, + "eventsSheetExpanded": false + } + ], + "parameters": [ + { + "description": "Object", + "name": "Object", + "supplementaryInformation": "ParticleEmitter3D::ParticleEmitter3D", + "type": "object" + } + ], + "objectGroups": [] + }, + { + "fullName": "Update particle image", + "functionType": "Action", + "name": "UpdateImage", + "private": true, + "sentence": "Update particle image of _PARAM0_", + "events": [ + { + "type": "BuiltinCommonInstructions::JsCode", + "inlineCode": [ + "/** @type {gdjs.CustomRuntimeObject3D} */\r", + "const object = objects[0];\r", + "/** @type {SpriteObjectDataType} */\r", + "const particleSpriteData = object._instanceContainer._objects.get(\"Particle\");\r", + "/** @type {gdjs.SpriteAnimationData} */\r", + "const animation = particleSpriteData.animations[0];\r", + "const animationFrame = animation ? animation.directions[0].sprites[0] : null;\r", + "const resourceName = animationFrame ? animationFrame.image : '';\r", + "object.__particleEmitterAdapter.setImage(resourceName);" + ], + "parameterObjects": "Object", + "useStrict": true, + "eventsSheetExpanded": true + } + ], + "parameters": [ + { + "description": "Object", + "name": "Object", + "supplementaryInformation": "ParticleEmitter3D::ParticleEmitter3D", + "type": "object" + } + ], + "objectGroups": [] + }, + { + "fullName": "", + "functionType": "Action", + "name": "doStepPostEvents", + "sentence": "", + "events": [ + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [ + { + "type": { + "value": "ParticleEmitter3D::ParticleEmitter3D::HasEnded" + }, + "parameters": [ + "Object", + "" + ] + } + ], + "actions": [ + { + "type": { + "value": "ParticleEmitter3D::ParticleEmitter3D::Delete" + }, + "parameters": [ + "Object", + "" + ] + } + ] + }, + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [ + { + "type": { + "inverted": true, + "value": "ParticleEmitter3D::ParticleEmitter3D::HasEnded" + }, + "parameters": [ + "Object", + "" + ] + } + ], + "actions": [ + { + "type": { + "value": "ParticleEmitter3D::ParticleEmitter3D::RegisterInLayer" + }, + "parameters": [ + "Object", + "" + ] + } + ], + "events": [ + { + "type": "BuiltinCommonInstructions::JsCode", + "inlineCode": [ + "/** @type {gdjs.CustomRuntimeObject3D} */", + "const object = objects[0];", + "// Here runtimeScene is the gdjs.CustomRuntimeObjectInstanceContainer inside the custom object.", + "const gameScene = object.getRuntimeScene();", + "", + "// Update batch system after having moved every emitter.", + "// See onScenePreEvents", + "gameScene.__particleEmmiter3DExtension.emittersStepped++;", + "if (gameScene.__particleEmmiter3DExtension.emittersStepped === gameScene.__particleEmmiter3DExtension.emittersCount) {", + " gameScene.__particleEmmiter3DExtension.layerNames = gameScene.__particleEmmiter3DExtension.layerNames || [];", + " const layerNames = gameScene.__particleEmmiter3DExtension.layerNames;", + " gameScene.getAllLayerNames(layerNames);", + " for (const layerName of layerNames) {", + " const layer = gameScene.getLayer(layerName);", + " if (layer.__particleEmmiter3DExtension) {", + " const timeDelta = layer.getElapsedTime() / 1000;", + " if (timeDelta > 0) {", + " layer.__particleEmmiter3DExtension.batchSystem.update(timeDelta);", + " }", + " }", + " }", + "}" + ], + "parameterObjects": "Object", + "useStrict": true, + "eventsSheetExpanded": true + } + ] + } + ], + "parameters": [ + { + "description": "Object", + "name": "Object", + "supplementaryInformation": "ParticleEmitter3D::ParticleEmitter3D", + "type": "object" + } + ], + "objectGroups": [] + }, + { + "fullName": "Register in layer", + "functionType": "Action", + "name": "RegisterInLayer", + "private": true, + "sentence": "Register _PARAM0_ in layer", + "events": [ + { + "type": "BuiltinCommonInstructions::JsCode", + "inlineCode": [ + "/** @type {gdjs.CustomRuntimeObject} */", + "const object = objects[0];", + "", + "// TODO Handle layer changes?", + "if (object.__emitterLayerName === undefined) {", + " // Here runtimeScene is the gdjs.CustomRuntimeObjectInstanceContainer inside the custom object.", + " const gameScene = object.getRuntimeScene();", + "", + " let rootObject = object;", + " while (rootObject.getInstanceContainer().getOwner) {", + " rootObject = rootObject.getInstanceContainer().getOwner();", + " }", + "", + " const layer = gameScene.getLayer(rootObject.getLayer());", + " layer.__particleEmmiter3DExtension = layer.__particleEmmiter3DExtension || {};", + " if (!layer.__particleEmmiter3DExtension.batchSystem) {", + " const batchSystem = new gdjs.__particleEmmiter3DExtension.BatchedRenderer();", + " batchSystem.scale.y *= -1;", + " const threeScene = layer.getRenderer().getThreeScene();", + " if (threeScene) {", + " threeScene.add(batchSystem);", + " }", + " layer.__particleEmmiter3DExtension.batchSystem = batchSystem;", + " }", + " layer.__particleEmmiter3DExtension.batchSystem.addSystem(object.__particleSystem);", + " object.__emitterLayerName = layer.getName();", + "}" + ], + "parameterObjects": "Object", + "useStrict": true, + "eventsSheetExpanded": true + } + ], + "parameters": [ + { + "description": "Object", + "name": "Object", + "supplementaryInformation": "ParticleEmitter3D::ParticleEmitter3D", + "type": "object" + } + ], + "objectGroups": [] + }, + { + "description": "Delete itself", + "fullName": "Delete itself", + "functionType": "Action", + "name": "Delete", + "private": true, + "sentence": "Delete _PARAM0_", + "events": [ + { + "type": "BuiltinCommonInstructions::JsCode", + "inlineCode": [ + "const object = objects[0];", + "", + "object.deleteFromScene(object.getParent());", + "" + ], + "parameterObjects": "Object", + "useStrict": true, + "eventsSheetExpanded": true + } + ], + "parameters": [ + { + "description": "Object", + "name": "Object", + "supplementaryInformation": "ParticleEmitter3D::ParticleEmitter3D", + "type": "object" + } + ], + "objectGroups": [] + }, + { + "description": "Check that emission has ended and no particle is alive anymore.", + "fullName": "Emission has ended", + "functionType": "Condition", + "name": "HasEnded", + "sentence": "Emission from _PARAM0_ has ended", + "events": [ + { + "type": "BuiltinCommonInstructions::JsCode", + "inlineCode": [ + "const object = objects[0];\r", + "\r", + "eventsFunctionContext.returnValue =\r", + " object._getShouldAutodestruct()\r", + " && object.__particleSystem.emitEnded\r", + " && object.__particleSystem.particleNum === 0;\r", + "" + ], + "parameterObjects": "Object", + "useStrict": true, + "eventsSheetExpanded": true + } + ], + "parameters": [ + { + "description": "Object", + "name": "Object", + "supplementaryInformation": "ParticleEmitter3D::ParticleEmitter3D", + "type": "object" + } + ], + "objectGroups": [] + }, + { + "description": "Restart particule emission from the beginning.", + "fullName": "Restart", + "functionType": "Action", + "name": "Restart", + "sentence": "Restart particule emission from _PARAM0_", + "events": [ + { + "type": "BuiltinCommonInstructions::JsCode", + "inlineCode": [ + "/** @type {gdjs.CustomRuntimeObject} */\r", + "const object = objects[0];\r", + "\r", + "object.__particleSystem.restart();\r", + "" + ], + "parameterObjects": "Object", + "useStrict": true, + "eventsSheetExpanded": false + } + ], + "parameters": [ + { + "description": "Object", + "name": "Object", + "supplementaryInformation": "ParticleEmitter3D::ParticleEmitter3D", + "type": "object" + } + ], + "objectGroups": [] + }, + { + "description": "the Z position of the emitter.", + "fullName": "Z elevaltion (deprecated)", + "functionType": "ExpressionAndCondition", + "group": "Position", + "name": "Z", + "private": true, + "sentence": "the Z position", + "events": [ + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [], + "actions": [ + { + "type": { + "value": "SetReturnNumber" + }, + "parameters": [ + "Object.PropertyZ()" + ] + } + ] + } + ], + "expressionType": { + "type": "expression" + }, + "parameters": [ + { + "description": "Object", + "name": "Object", + "supplementaryInformation": "ParticleEmitter3D::ParticleEmitter3D", + "type": "object" + } + ], + "objectGroups": [] + }, + { + "fullName": "", + "functionType": "ActionWithOperator", + "getterName": "Z", + "group": "Position", + "name": "SetZ", + "private": true, + "sentence": "", + "events": [ + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [], + "actions": [ + { + "type": { + "value": "SetNumberVariable" + }, + "parameters": [ + "Z", + "=", + "Value" + ] + } + ] + } + ], + "parameters": [ + { + "description": "Object", + "name": "Object", + "supplementaryInformation": "ParticleEmitter3D::ParticleEmitter3D", + "type": "object" + } + ], + "objectGroups": [] + }, + { + "description": "the rotation on X axis of the emitter.", + "fullName": "Rotation on X axis (deprecated)", + "functionType": "ExpressionAndCondition", + "group": "Angle", + "name": "RotationX", + "private": true, + "sentence": "the rotation on X axis", + "events": [ + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [], + "actions": [ + { + "type": { + "value": "SetReturnNumber" + }, + "parameters": [ + "Object.PropertyRotationX()" + ] + } + ] + } + ], + "expressionType": { + "type": "expression" + }, + "parameters": [ + { + "description": "Object", + "name": "Object", + "supplementaryInformation": "ParticleEmitter3D::ParticleEmitter3D", + "type": "object" + } + ], + "objectGroups": [] + }, + { + "fullName": "", + "functionType": "ActionWithOperator", + "getterName": "RotationX", + "group": "Angle", + "name": "SetRotationX", + "private": true, + "sentence": "", + "events": [ + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [], + "actions": [ + { + "type": { + "value": "SetNumberVariable" + }, + "parameters": [ + "RotationX", + "=", + "Value" + ] + } + ] + }, + { + "type": "BuiltinCommonInstructions::JsCode", + "inlineCode": [ + "const object = objects[0];\r", + "\r", + "object.__particleSystem.emitter.rotation.x = eventsFunctionContext.getArgument(\"Value\") * Math.PI / 180;" + ], + "parameterObjects": "Object", + "useStrict": true, + "eventsSheetExpanded": false + } + ], + "parameters": [ + { + "description": "Object", + "name": "Object", + "supplementaryInformation": "ParticleEmitter3D::ParticleEmitter3D", + "type": "object" + } + ], + "objectGroups": [] + }, + { + "description": "the rotation on Y axis of the emitter.", + "fullName": "Rotation on Y axis (deprecated)", + "functionType": "ExpressionAndCondition", + "group": "Angle", + "name": "RotationY", + "private": true, + "sentence": "the rotation on Y axis", + "events": [ + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [], + "actions": [ + { + "type": { + "value": "SetReturnNumber" + }, + "parameters": [ + "Object.PropertyRotationY()" + ] + } + ] + } + ], + "expressionType": { + "type": "expression" + }, + "parameters": [ + { + "description": "Object", + "name": "Object", + "supplementaryInformation": "ParticleEmitter3D::ParticleEmitter3D", + "type": "object" + } + ], + "objectGroups": [] + }, + { + "fullName": "", + "functionType": "ActionWithOperator", + "getterName": "RotationY", + "group": "Angle", + "name": "SetRotationY", + "private": true, + "sentence": "", + "events": [ + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [], + "actions": [ + { + "type": { + "value": "SetNumberVariable" + }, + "parameters": [ + "RotationY", + "=", + "Value" + ] + } + ] + }, + { + "type": "BuiltinCommonInstructions::JsCode", + "inlineCode": [ + "const object = objects[0];\r", + "\r", + "object.__particleSystem.emitter.rotation.y = (90 + eventsFunctionContext.getArgument(\"Value\")) * Math.PI / 180;" + ], + "parameterObjects": "Object", + "useStrict": true, + "eventsSheetExpanded": false + } + ], + "parameters": [ + { + "description": "Object", + "name": "Object", + "supplementaryInformation": "ParticleEmitter3D::ParticleEmitter3D", + "type": "object" + } + ], + "objectGroups": [] + }, + { + "description": "the start color of the object.", + "fullName": "Start color", + "functionType": "ExpressionAndCondition", + "group": "3D particle emitter color configuration", + "name": "StartColor", + "sentence": "the start color", + "events": [ + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [], + "actions": [ + { + "type": { + "value": "SetReturnString" + }, + "parameters": [ + "StartColor" + ] + } + ] + } + ], + "expressionType": { + "type": "color" + }, + "parameters": [ + { + "description": "Object", + "name": "Object", + "supplementaryInformation": "ParticleEmitter3D::ParticleEmitter3D", + "type": "object" + } + ], + "objectGroups": [] + }, + { + "fullName": "", + "functionType": "ActionWithOperator", + "getterName": "StartColor", + "group": "3D particle emitter color configuration", + "name": "SetStartColor", + "sentence": "", + "events": [ + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [], + "actions": [ + { + "type": { + "value": "SetStringVariable" + }, + "parameters": [ + "StartColor", + "=", + "Value" + ] + } + ] + }, + { + "type": "BuiltinCommonInstructions::JsCode", + "inlineCode": [ + "const object = objects[0];\r", + "const startColor = eventsFunctionContext.getArgument(\"Value\");\r", + "\r", + "object.__particleEmitterAdapter.setStartColor(startColor);" + ], + "parameterObjects": "Object", + "useStrict": true, + "eventsSheetExpanded": false + } + ], + "parameters": [ + { + "description": "Object", + "name": "Object", + "supplementaryInformation": "ParticleEmitter3D::ParticleEmitter3D", + "type": "object" + } + ], + "objectGroups": [] + }, + { + "description": "the end color of the object.", + "fullName": "End color", + "functionType": "ExpressionAndCondition", + "group": "3D particle emitter color configuration", + "name": "EndColor", + "sentence": "the end color", + "events": [ + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [], + "actions": [ + { + "type": { + "value": "SetReturnString" + }, + "parameters": [ + "EndColor" + ] + } + ] + } + ], + "expressionType": { + "type": "color" + }, + "parameters": [ + { + "description": "Object", + "name": "Object", + "supplementaryInformation": "ParticleEmitter3D::ParticleEmitter3D", + "type": "object" + } + ], + "objectGroups": [] + }, + { + "fullName": "", + "functionType": "ActionWithOperator", + "getterName": "EndColor", + "group": "3D particle emitter color configuration", + "name": "SetEndColor", + "sentence": "", + "events": [ + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [], + "actions": [ + { + "type": { + "value": "SetStringVariable" + }, + "parameters": [ + "EndColor", + "=", + "Value" + ] + } + ] + }, + { + "type": "BuiltinCommonInstructions::JsCode", + "inlineCode": [ + "const object = objects[0];\r", + "const endColor = eventsFunctionContext.getArgument(\"Value\");\r", + "\r", + "object.__particleEmitterAdapter.setEndColor(endColor);" + ], + "parameterObjects": "Object", + "useStrict": true, + "eventsSheetExpanded": false + } + ], + "parameters": [ + { + "description": "Object", + "name": "Object", + "supplementaryInformation": "ParticleEmitter3D::ParticleEmitter3D", + "type": "object" + } + ], + "objectGroups": [] + }, + { + "description": "the start opacity of the object.", + "fullName": "Start opacity", + "functionType": "ExpressionAndCondition", + "group": "3D particle emitter color configuration", + "name": "StartOpacity", + "sentence": "the start opacity", + "events": [ + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [], + "actions": [ + { + "type": { + "value": "SetReturnNumber" + }, + "parameters": [ + "StartOpacity" + ] + } + ] + } + ], + "expressionType": { + "type": "expression" + }, + "parameters": [ + { + "description": "Object", + "name": "Object", + "supplementaryInformation": "ParticleEmitter3D::ParticleEmitter3D", + "type": "object" + } + ], + "objectGroups": [] + }, + { + "fullName": "", + "functionType": "ActionWithOperator", + "getterName": "StartOpacity", + "group": "3D particle emitter color configuration", + "name": "SetStartOpacity", + "sentence": "", + "events": [ + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [], + "actions": [ + { + "type": { + "value": "SetNumberVariable" + }, + "parameters": [ + "StartOpacity", + "=", + "Value" + ] + } + ] + }, + { + "type": "BuiltinCommonInstructions::JsCode", + "inlineCode": [ + "const object = objects[0];\r", + "const startOpacity = eventsFunctionContext.getArgument(\"Value\");\r", + "\r", + "object.__particleEmitterAdapter.setStartOpacity(startOpacity);" + ], + "parameterObjects": "Object", + "useStrict": true, + "eventsSheetExpanded": false + } + ], + "parameters": [ + { + "description": "Object", + "name": "Object", + "supplementaryInformation": "ParticleEmitter3D::ParticleEmitter3D", + "type": "object" + } + ], + "objectGroups": [] + }, + { + "description": "the end opacity of the object.", + "fullName": "End opacity", + "functionType": "ExpressionAndCondition", + "group": "3D particle emitter color configuration", + "name": "EndOpacity", + "sentence": "the end opacity", + "events": [ + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [], + "actions": [ + { + "type": { + "value": "SetReturnNumber" + }, + "parameters": [ + "EndOpacity" + ] + } + ] + } + ], + "expressionType": { + "type": "expression" + }, + "parameters": [ + { + "description": "Object", + "name": "Object", + "supplementaryInformation": "ParticleEmitter3D::ParticleEmitter3D", + "type": "object" + } + ], + "objectGroups": [] + }, + { + "fullName": "", + "functionType": "ActionWithOperator", + "getterName": "EndOpacity", + "group": "3D particle emitter color configuration", + "name": "SetEndOpacity", + "sentence": "", + "events": [ + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [], + "actions": [ + { + "type": { + "value": "SetNumberVariable" + }, + "parameters": [ + "EndOpacity", + "=", + "Value" + ] + } + ] + }, + { + "type": "BuiltinCommonInstructions::JsCode", + "inlineCode": [ + "const object = objects[0];\r", + "const endOpacity = eventsFunctionContext.getArgument(\"Value\");\r", + "\r", + "object.__particleEmitterAdapter.setEndOpacity(endOpacity);" + ], + "parameterObjects": "Object", + "useStrict": true, + "eventsSheetExpanded": false + } + ], + "parameters": [ + { + "description": "Object", + "name": "Object", + "supplementaryInformation": "ParticleEmitter3D::ParticleEmitter3D", + "type": "object" + } + ], + "objectGroups": [] + }, + { + "description": "the flow of particles of the object (particles per second).", + "fullName": "Flow of particles", + "functionType": "ExpressionAndCondition", + "group": "3D particle emitter configuration", + "name": "Flow", + "sentence": "the flow of particles", + "events": [ + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [], + "actions": [ + { + "type": { + "value": "SetReturnNumber" + }, + "parameters": [ + "Flow" + ] + } + ] + } + ], + "expressionType": { + "type": "expression" + }, + "parameters": [ + { + "description": "Object", + "name": "Object", + "supplementaryInformation": "ParticleEmitter3D::ParticleEmitter3D", + "type": "object" + } + ], + "objectGroups": [] + }, + { + "fullName": "", + "functionType": "ActionWithOperator", + "getterName": "Flow", + "group": "3D particle emitter configuration", + "name": "SetFlow", + "sentence": "", + "events": [ + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [], + "actions": [ + { + "type": { + "value": "SetNumberVariable" + }, + "parameters": [ + "Flow", + "=", + "Value" + ] + } + ] + }, + { + "type": "BuiltinCommonInstructions::JsCode", + "inlineCode": [ + "const object = objects[0];\r", + "const flow = eventsFunctionContext.getArgument(\"Value\");\r", + "\r", + "object.__particleEmitterAdapter.setFlow(flow);\r", + "" + ], + "parameterObjects": "Object", + "useStrict": true, + "eventsSheetExpanded": false + } + ], + "parameters": [ + { + "description": "Object", + "name": "Object", + "supplementaryInformation": "ParticleEmitter3D::ParticleEmitter3D", + "type": "object" + } + ], + "objectGroups": [] + }, + { + "description": "the start min size of the object.", + "fullName": "Start min size", + "functionType": "ExpressionAndCondition", + "group": "3D particle emitter configuration", + "name": "StartSizeMin", + "sentence": "the start min size", + "events": [ + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [], + "actions": [ + { + "type": { + "value": "SetReturnNumber" + }, + "parameters": [ + "StartSizeMin" + ] + } + ] + } + ], + "expressionType": { + "type": "expression" + }, + "parameters": [ + { + "description": "Object", + "name": "Object", + "supplementaryInformation": "ParticleEmitter3D::ParticleEmitter3D", + "type": "object" + } + ], + "objectGroups": [] + }, + { + "fullName": "", + "functionType": "ActionWithOperator", + "getterName": "StartSizeMin", + "group": "3D particle emitter configuration", + "name": "SetStartSizeMin", + "sentence": "", + "events": [ + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [], + "actions": [ + { + "type": { + "value": "SetNumberVariable" + }, + "parameters": [ + "StartSizeMin", + "=", + "Value" + ] + } + ] + }, + { + "type": "BuiltinCommonInstructions::JsCode", + "inlineCode": [ + "const object = objects[0];\r", + "const startMinSize = eventsFunctionContext.getArgument(\"Value\");\r", + "\r", + "object.__particleEmitterAdapter.setStartMinSize(startMinSize);" + ], + "parameterObjects": "Object", + "useStrict": true, + "eventsSheetExpanded": false + } + ], + "parameters": [ + { + "description": "Object", + "name": "Object", + "supplementaryInformation": "ParticleEmitter3D::ParticleEmitter3D", + "type": "object" + } + ], + "objectGroups": [] + }, + { + "description": "the start max size of the object.", + "fullName": "Start max size", + "functionType": "ExpressionAndCondition", + "group": "3D particle emitter configuration", + "name": "StartSizeMax", + "sentence": "the start max size", + "events": [ + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [], + "actions": [ + { + "type": { + "value": "SetReturnNumber" + }, + "parameters": [ + "StartSizeMax" + ] + } + ] + } + ], + "expressionType": { + "type": "expression" + }, + "parameters": [ + { + "description": "Object", + "name": "Object", + "supplementaryInformation": "ParticleEmitter3D::ParticleEmitter3D", + "type": "object" + } + ], + "objectGroups": [] + }, + { + "fullName": "", + "functionType": "ActionWithOperator", + "getterName": "StartSizeMax", + "group": "3D particle emitter configuration", + "name": "SetStartSizeMax", + "sentence": "", + "events": [ + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [], + "actions": [ + { + "type": { + "value": "SetNumberVariable" + }, + "parameters": [ + "StartSizeMax", + "=", + "Value" + ] + } + ] + }, + { + "type": "BuiltinCommonInstructions::JsCode", + "inlineCode": [ + "const object = objects[0];\r", + "const startMaxSize = eventsFunctionContext.getArgument(\"Value\");\r", + "\r", + "object.__particleEmitterAdapter.setStartMaxSize(startMaxSize);\r", + "" + ], + "parameterObjects": "Object", + "useStrict": true, + "eventsSheetExpanded": false + } + ], + "parameters": [ + { + "description": "Object", + "name": "Object", + "supplementaryInformation": "ParticleEmitter3D::ParticleEmitter3D", + "type": "object" + } + ], + "objectGroups": [] + }, + { + "description": "the end scale of the object.", + "fullName": "End scale", + "functionType": "ExpressionAndCondition", + "group": "3D particle emitter configuration", + "name": "EndScale", + "sentence": "the end scale", + "events": [ + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [], + "actions": [ + { + "type": { + "value": "SetReturnNumber" + }, + "parameters": [ + "EndScale" + ] + } + ] + } + ], + "expressionType": { + "type": "expression" + }, + "parameters": [ + { + "description": "Object", + "name": "Object", + "supplementaryInformation": "ParticleEmitter3D::ParticleEmitter3D", + "type": "object" + } + ], + "objectGroups": [] + }, + { + "fullName": "", + "functionType": "ActionWithOperator", + "getterName": "EndScale", + "group": "3D particle emitter configuration", + "name": "SetEndScale", + "sentence": "", + "events": [ + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [], + "actions": [ + { + "type": { + "value": "SetNumberVariable" + }, + "parameters": [ + "EndScale", + "=", + "Value" + ] + } + ] + }, + { + "type": "BuiltinCommonInstructions::JsCode", + "inlineCode": [ + "const object = objects[0];\r", + "const endScale = eventsFunctionContext.getArgument(\"Value\");\r", + "\r", + "object.__particleEmitterAdapter.setEndScale(endScale);\r", + "" + ], + "parameterObjects": "Object", + "useStrict": true, + "eventsSheetExpanded": false + } + ], + "parameters": [ + { + "description": "Object", + "name": "Object", + "supplementaryInformation": "ParticleEmitter3D::ParticleEmitter3D", + "type": "object" + } + ], + "objectGroups": [] + }, + { + "description": "the min start speed of the object.", + "fullName": "Min start speed", + "functionType": "ExpressionAndCondition", + "group": "3D particle emitter speed configuration", + "name": "StartSpeedMin", + "sentence": "the min start speed", + "events": [ + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [], + "actions": [ + { + "type": { + "value": "SetReturnNumber" + }, + "parameters": [ + "StartSpeedMin" + ] + } + ] + } + ], + "expressionType": { + "type": "expression" + }, + "parameters": [ + { + "description": "Object", + "name": "Object", + "supplementaryInformation": "ParticleEmitter3D::ParticleEmitter3D", + "type": "object" + } + ], + "objectGroups": [] + }, + { + "fullName": "", + "functionType": "ActionWithOperator", + "getterName": "StartSpeedMin", + "group": "3D particle emitter speed configuration", + "name": "SetStartSpeedMin", + "sentence": "", + "events": [ + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [], + "actions": [ + { + "type": { + "value": "SetNumberVariable" + }, + "parameters": [ + "StartSpeedMin", + "=", + "Value" + ] + } + ] + }, + { + "type": "BuiltinCommonInstructions::JsCode", + "inlineCode": [ + "const object = objects[0];\r", + "const startSpeedMin = eventsFunctionContext.getArgument(\"Value\");\r", + "\r", + "object.__particleEmitterAdapter.setStartSpeedMin(startSpeedMin);" + ], + "parameterObjects": "Object", + "useStrict": true, + "eventsSheetExpanded": false + } + ], + "parameters": [ + { + "description": "Object", + "name": "Object", + "supplementaryInformation": "ParticleEmitter3D::ParticleEmitter3D", + "type": "object" + } + ], + "objectGroups": [] + }, + { + "description": "the max start speed of the object.", + "fullName": "Max start speed", + "functionType": "ExpressionAndCondition", + "group": "3D particle emitter speed configuration", + "name": "StartSpeedMax", + "sentence": "the max start speed", + "events": [ + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [], + "actions": [ + { + "type": { + "value": "SetReturnNumber" + }, + "parameters": [ + "StartSpeedMax" + ] + } + ] + } + ], + "expressionType": { + "type": "expression" + }, + "parameters": [ + { + "description": "Object", + "name": "Object", + "supplementaryInformation": "ParticleEmitter3D::ParticleEmitter3D", + "type": "object" + } + ], + "objectGroups": [] + }, + { + "fullName": "", + "functionType": "ActionWithOperator", + "getterName": "StartSpeedMax", + "group": "3D particle emitter speed configuration", + "name": "SetStartSpeedMax", + "sentence": "", + "events": [ + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [], + "actions": [ + { + "type": { + "value": "SetNumberVariable" + }, + "parameters": [ + "StartSpeedMax", + "=", + "Value" + ] + } + ] + }, + { + "type": "BuiltinCommonInstructions::JsCode", + "inlineCode": [ + "const object = objects[0];\r", + "const startSpeedMax = eventsFunctionContext.getArgument(\"Value\");\r", + "\r", + "object.__particleEmitterAdapter.setStartSpeedMax(startSpeedMax);" + ], + "parameterObjects": "Object", + "useStrict": true, + "eventsSheetExpanded": false + } + ], + "parameters": [ + { + "description": "Object", + "name": "Object", + "supplementaryInformation": "ParticleEmitter3D::ParticleEmitter3D", + "type": "object" + } + ], + "objectGroups": [] + }, + { + "description": "the min lifespan of the object.", + "fullName": "Min lifespan", + "functionType": "ExpressionAndCondition", + "group": "3D particle emitter configuration", + "name": "LifespanMin", + "sentence": "the min lifespan", + "events": [ + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [], + "actions": [ + { + "type": { + "value": "SetReturnNumber" + }, + "parameters": [ + "LifespanMin" + ] + } + ] + } + ], + "expressionType": { + "type": "expression" + }, + "parameters": [ + { + "description": "Object", + "name": "Object", + "supplementaryInformation": "ParticleEmitter3D::ParticleEmitter3D", + "type": "object" + } + ], + "objectGroups": [] + }, + { + "fullName": "", + "functionType": "ActionWithOperator", + "getterName": "LifespanMin", + "group": "3D particle emitter configuration", + "name": "SetLifespanMin", + "sentence": "", + "events": [ + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [], + "actions": [ + { + "type": { + "value": "SetNumberVariable" + }, + "parameters": [ + "LifespanMin", + "=", + "Value" + ] + } + ] + }, + { + "type": "BuiltinCommonInstructions::JsCode", + "inlineCode": [ + "const object = objects[0];\r", + "const lifespanMin = eventsFunctionContext.getArgument(\"Value\");\r", + "\r", + "object.__particleEmitterAdapter.setLifespanMin(lifespanMin);\r", + "" + ], + "parameterObjects": "Object", + "useStrict": true, + "eventsSheetExpanded": false + } + ], + "parameters": [ + { + "description": "Object", + "name": "Object", + "supplementaryInformation": "ParticleEmitter3D::ParticleEmitter3D", + "type": "object" + } + ], + "objectGroups": [] + }, + { + "description": "the max lifespan of the object.", + "fullName": "Max lifespan", + "functionType": "ExpressionAndCondition", + "group": "3D particle emitter configuration", + "name": "LifespanMax", + "sentence": "the max lifespan", + "events": [ + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [], + "actions": [ + { + "type": { + "value": "SetReturnNumber" + }, + "parameters": [ + "LifespanMax" + ] + } + ] + } + ], + "expressionType": { + "type": "expression" + }, + "parameters": [ + { + "description": "Object", + "name": "Object", + "supplementaryInformation": "ParticleEmitter3D::ParticleEmitter3D", + "type": "object" + } + ], + "objectGroups": [] + }, + { + "fullName": "", + "functionType": "ActionWithOperator", + "getterName": "LifespanMax", + "group": "3D particle emitter configuration", + "name": "SetLifespanMax", + "sentence": "", + "events": [ + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [], + "actions": [ + { + "type": { + "value": "SetNumberVariable" + }, + "parameters": [ + "LifespanMax", + "=", + "Value" + ] + } + ] + }, + { + "type": "BuiltinCommonInstructions::JsCode", + "inlineCode": [ + "const object = objects[0];\r", + "const lifespanMax = eventsFunctionContext.getArgument(\"Value\");\r", + "\r", + "object.__particleEmitterAdapter.setLifespanMax(lifespanMax);\r", + "" + ], + "parameterObjects": "Object", + "useStrict": true, + "eventsSheetExpanded": false + } + ], + "parameters": [ + { + "description": "Object", + "name": "Object", + "supplementaryInformation": "ParticleEmitter3D::ParticleEmitter3D", + "type": "object" + } + ], + "objectGroups": [] + }, + { + "description": "the emission duration of the object.", + "fullName": "Emission duration", + "functionType": "ExpressionAndCondition", + "group": "3D particle emitter configuration", + "name": "Duration", + "sentence": "the emission duration", + "events": [ + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [], + "actions": [ + { + "type": { + "value": "SetReturnNumber" + }, + "parameters": [ + "Duration" + ] + } + ] + } + ], + "expressionType": { + "type": "expression" + }, + "parameters": [ + { + "description": "Object", + "name": "Object", + "supplementaryInformation": "ParticleEmitter3D::ParticleEmitter3D", + "type": "object" + } + ], + "objectGroups": [] + }, + { + "fullName": "", + "functionType": "ActionWithOperator", + "getterName": "Duration", + "group": "3D particle emitter configuration", + "name": "SetDuration", + "sentence": "", + "events": [ + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [], + "actions": [ + { + "type": { + "value": "SetNumberVariable" + }, + "parameters": [ + "Duration", + "=", + "Value" + ] + } + ] + }, + { + "type": "BuiltinCommonInstructions::JsCode", + "inlineCode": [ + "const object = objects[0];\r", + "const duration = eventsFunctionContext.getArgument(\"Value\");\r", + "\r", + "object.__particleEmitterAdapter.setDuration(duration);\r", + "" + ], + "parameterObjects": "Object", + "useStrict": true, + "eventsSheetExpanded": false + } + ], + "parameters": [ + { + "description": "Object", + "name": "Object", + "supplementaryInformation": "ParticleEmitter3D::ParticleEmitter3D", + "type": "object" + } + ], + "objectGroups": [] + }, + { + "description": "Check if particles move with the emitter.", + "fullName": "Particles move with the emitter", + "functionType": "Condition", + "group": "3D particle emitter speed configuration", + "name": "AreParticlesRelative", + "sentence": "_PARAM0_ particles move with the emitter", + "events": [ + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [ + { + "type": { + "value": "BooleanVariable" + }, + "parameters": [ + "AreParticlesRelative", + "True", + "" + ] + } + ], + "actions": [ + { + "type": { + "value": "SetReturnBoolean" + }, + "parameters": [ + "True" + ] + } + ] + } + ], + "parameters": [ + { + "description": "Object", + "name": "Object", + "supplementaryInformation": "ParticleEmitter3D::ParticleEmitter3D", + "type": "object" + } + ], + "objectGroups": [] + }, + { + "description": "Change if particles move with the emitter.", + "fullName": "Particles move with the emitter", + "functionType": "Action", + "group": "3D particle emitter speed configuration", + "name": "SetAreParticlesRelative", + "sentence": "_PARAM0_ particles move with the emitter: _PARAM1_", + "events": [ + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [ + { + "type": { + "inverted": true, + "value": "BooleanVariable" + }, + "parameters": [ + "Value", + "True", + "" + ] + } + ], + "actions": [ + { + "type": { + "value": "SetBooleanVariable" + }, + "parameters": [ + "AreParticlesRelative", + "False", + "" + ] + } + ] + }, + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [ + { + "type": { + "value": "BooleanVariable" + }, + "parameters": [ + "Value", + "True", + "" + ] + } + ], + "actions": [ + { + "type": { + "value": "SetBooleanVariable" + }, + "parameters": [ + "AreParticlesRelative", + "True", + "" + ] + } + ] + }, + { + "type": "BuiltinCommonInstructions::JsCode", + "inlineCode": [ + "const object = objects[0];\r", + "const areParticlesRelative = eventsFunctionContext.getArgument(\"Value\");\r", + "\r", + "object.__particleEmitterAdapter.setParticlesRelative(areParticlesRelative);\r", + "" + ], + "parameterObjects": "Object", + "useStrict": true, + "eventsSheetExpanded": false + } + ], + "parameters": [ + { + "description": "Object", + "name": "Object", + "supplementaryInformation": "ParticleEmitter3D::ParticleEmitter3D", + "type": "object" + }, + { + "defaultValue": "yes", + "description": "AreParticlesRelative", + "name": "Value", + "optional": true, + "type": "yesorno" + } + ], + "objectGroups": [] + }, + { + "description": "the spay cone angle of the object.", + "fullName": "Spay cone angle", + "functionType": "ExpressionAndCondition", + "group": "3D particle emitter configuration", + "name": "SpayConeAngle", + "sentence": "the spay cone angle", + "events": [ + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [], + "actions": [ + { + "type": { + "value": "SetReturnNumber" + }, + "parameters": [ + "SpayConeAngle" + ] + } + ] + } + ], + "expressionType": { + "type": "expression" + }, + "parameters": [ + { + "description": "Object", + "name": "Object", + "supplementaryInformation": "ParticleEmitter3D::ParticleEmitter3D", + "type": "object" + } + ], + "objectGroups": [] + }, + { + "fullName": "", + "functionType": "ActionWithOperator", + "getterName": "SpayConeAngle", + "group": "3D particle emitter configuration", + "name": "SetSpayConeAngle", + "sentence": "", + "events": [ + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [], + "actions": [ + { + "type": { + "value": "SetNumberVariable" + }, + "parameters": [ + "SpayConeAngle", + "=", + "Value" + ] + } + ] + }, + { + "type": "BuiltinCommonInstructions::JsCode", + "inlineCode": [ + "const object = objects[0];\r", + "const sprayConeAngle = eventsFunctionContext.getArgument(\"Value\");\r", + "\r", + "object.__particleEmitterAdapter.setSprayConeAngle(sprayConeAngle);\r", + "" + ], + "parameterObjects": "Object", + "useStrict": true, + "eventsSheetExpanded": true + } + ], + "parameters": [ + { + "description": "Object", + "name": "Object", + "supplementaryInformation": "ParticleEmitter3D::ParticleEmitter3D", + "type": "object" + } + ], + "objectGroups": [] + }, + { + "description": "the blending of the object.", + "fullName": "Blending", + "functionType": "ExpressionAndCondition", + "group": "3D particle emitter color configuration", + "name": "Blending", + "sentence": "the blending", + "events": [ + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [], + "actions": [ + { + "type": { + "value": "SetReturnString" + }, + "parameters": [ + "Blending" + ] + } + ] + } + ], + "expressionType": { + "supplementaryInformation": "[\"Normal\",\"Additive\",\"Substractive\",\"Multiply\",\"None\"]", + "type": "stringWithSelector" + }, + "parameters": [ + { + "description": "Object", + "name": "Object", + "supplementaryInformation": "ParticleEmitter3D::ParticleEmitter3D", + "type": "object" + } + ], + "objectGroups": [] + }, + { + "fullName": "", + "functionType": "ActionWithOperator", + "getterName": "Blending", + "group": "3D particle emitter color configuration", + "name": "SetBlending", + "private": true, + "sentence": "", + "events": [ + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [], + "actions": [ + { + "type": { + "value": "SetStringVariable" + }, + "parameters": [ + "Blending", + "=", + "Value" + ] + } + ] + }, + { + "type": "BuiltinCommonInstructions::JsCode", + "inlineCode": [ + "const object = objects[0];\r", + "const blending = eventsFunctionContext.getArgument(\"Value\");\r", + "\r", + "object.__particleEmitterAdapter.setBlending(blending);\r", + "" + ], + "parameterObjects": "Object", + "useStrict": true, + "eventsSheetExpanded": true + } + ], + "parameters": [ + { + "description": "Object", + "name": "Object", + "supplementaryInformation": "ParticleEmitter3D::ParticleEmitter3D", + "type": "object" + } + ], + "objectGroups": [] + }, + { + "description": "the gravity top of the object.", + "fullName": "Gravity top", + "functionType": "ExpressionAndCondition", + "group": "3D particle emitter speed configuration", + "name": "GravityTop", + "sentence": "the gravity top", + "events": [ + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [], + "actions": [ + { + "type": { + "value": "SetReturnString" + }, + "parameters": [ + "GravityTop" + ] + } + ] + } + ], + "expressionType": { + "supplementaryInformation": "[\"Y-\",\"Z+\"]", + "type": "stringWithSelector" + }, + "parameters": [ + { + "description": "Object", + "name": "Object", + "supplementaryInformation": "ParticleEmitter3D::ParticleEmitter3D", + "type": "object" + } + ], + "objectGroups": [] + }, + { + "fullName": "", + "functionType": "ActionWithOperator", + "getterName": "GravityTop", + "group": "3D particle emitter speed configuration", + "name": "SetGravityTop", + "sentence": "", + "events": [ + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [], + "actions": [ + { + "type": { + "value": "SetStringVariable" + }, + "parameters": [ + "GravityTop", + "=", + "Value" + ] + } + ] + }, + { + "type": "BuiltinCommonInstructions::JsCode", + "inlineCode": [ + "const object = objects[0];\r", + "const gravityTop = eventsFunctionContext.getArgument(\"Value\");\r", + "\r", + "object.__particleEmitterAdapter.setGravityTop(gravityTop);\r", + "" + ], + "parameterObjects": "Object", + "useStrict": true, + "eventsSheetExpanded": true + } + ], + "parameters": [ + { + "description": "Object", + "name": "Object", + "supplementaryInformation": "ParticleEmitter3D::ParticleEmitter3D", + "type": "object" + } + ], + "objectGroups": [] + }, + { + "description": "the gravity of the object.", + "fullName": "Gravity", + "functionType": "ExpressionAndCondition", + "group": "3D particle emitter speed configuration", + "name": "Gravity", + "sentence": "the gravity", + "events": [ + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [], + "actions": [ + { + "type": { + "value": "SetReturnNumber" + }, + "parameters": [ + "Gravity" + ] + } + ] + } + ], + "expressionType": { + "type": "expression" + }, + "parameters": [ + { + "description": "Object", + "name": "Object", + "supplementaryInformation": "ParticleEmitter3D::ParticleEmitter3D", + "type": "object" + } + ], + "objectGroups": [] + }, + { + "fullName": "", + "functionType": "ActionWithOperator", + "getterName": "Gravity", + "group": "3D particle emitter speed configuration", + "name": "SetGravity", + "sentence": "", + "events": [ + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [], + "actions": [ + { + "type": { + "value": "SetNumberVariable" + }, + "parameters": [ + "Gravity", + "=", + "Value" + ] + } + ] + }, + { + "type": "BuiltinCommonInstructions::JsCode", + "inlineCode": [ + "const object = objects[0];\r", + "const gravity = eventsFunctionContext.getArgument(\"Value\");\r", + "\r", + "object.__particleEmitterAdapter.setGravity(gravity);\r", + "" + ], + "parameterObjects": "Object", + "useStrict": true, + "eventsSheetExpanded": true + } + ], + "parameters": [ + { + "description": "Object", + "name": "Object", + "supplementaryInformation": "ParticleEmitter3D::ParticleEmitter3D", + "type": "object" + } + ], + "objectGroups": [] + }, + { + "description": "Check if delete when emission ends.", + "fullName": "Delete when emission ends", + "functionType": "Condition", + "group": "3D particle emitter configuration", + "name": "ShouldAutodestruct", + "sentence": "_PARAM0_ delete when emission ends", + "events": [ + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [ + { + "type": { + "value": "BooleanVariable" + }, + "parameters": [ + "ShouldAutodestruct", + "True", + "" + ] + } + ], + "actions": [ + { + "type": { + "value": "SetReturnBoolean" + }, + "parameters": [ + "True" + ] + } + ] + } + ], + "parameters": [ + { + "description": "Object", + "name": "Object", + "supplementaryInformation": "ParticleEmitter3D::ParticleEmitter3D", + "type": "object" + } + ], + "objectGroups": [] + }, + { + "description": "Change if delete when emission ends.", + "fullName": "Delete when emission ends", + "functionType": "Action", + "group": "3D particle emitter configuration", + "name": "SetShouldAutodestruct", + "sentence": "_PARAM0_ delete when emission ends: _PARAM1_", + "events": [ + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [ + { + "type": { + "inverted": true, + "value": "BooleanVariable" + }, + "parameters": [ + "Value", + "True", + "" + ] + } + ], + "actions": [ + { + "type": { + "value": "SetBooleanVariable" + }, + "parameters": [ + "ShouldAutodestruct", + "False", + "" + ] + } + ] + }, + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [ + { + "type": { + "value": "BooleanVariable" + }, + "parameters": [ + "Value", + "True", + "" + ] + } + ], + "actions": [ + { + "type": { + "value": "SetBooleanVariable" + }, + "parameters": [ + "ShouldAutodestruct", + "True", + "" + ] + } + ] + } + ], + "parameters": [ + { + "description": "Object", + "name": "Object", + "supplementaryInformation": "ParticleEmitter3D::ParticleEmitter3D", + "type": "object" + }, + { + "defaultValue": "yes", + "description": "ShouldAutodestruct", + "name": "Value", + "optional": true, + "type": "yesorno" + } + ], + "objectGroups": [] + }, + { + "description": "the start min trail length of the object.", + "fullName": "Start min trail length", + "functionType": "ExpressionAndCondition", + "group": "3D particle emitter trail configuration", + "name": "TrailStartLengthMin", + "sentence": "the start min trail length", + "events": [ + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [], + "actions": [ + { + "type": { + "value": "SetReturnNumber" + }, + "parameters": [ + "TrailStartLengthMin" + ] + } + ] + } + ], + "expressionType": { + "type": "expression" + }, + "parameters": [ + { + "description": "Object", + "name": "Object", + "supplementaryInformation": "ParticleEmitter3D::ParticleEmitter3D", + "type": "object" + } + ], + "objectGroups": [] + }, + { + "fullName": "", + "functionType": "ActionWithOperator", + "getterName": "TrailStartLengthMin", + "group": "3D particle emitter trail configuration", + "name": "SetTrailStartLengthMin", + "sentence": "", + "events": [ + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [], + "actions": [ + { + "type": { + "value": "SetNumberVariable" + }, + "parameters": [ + "TrailStartLengthMin", + "=", + "Value" + ] + } + ] + }, + { + "type": "BuiltinCommonInstructions::JsCode", + "inlineCode": [ + "const object = objects[0];\r", + "const value = eventsFunctionContext.getArgument(\"Value\");\r", + "\r", + "object.__particleEmitterAdapter.setTrailStartLengthMin(value);" + ], + "parameterObjects": "Object", + "useStrict": true, + "eventsSheetExpanded": false + } + ], + "parameters": [ + { + "description": "Object", + "name": "Object", + "supplementaryInformation": "ParticleEmitter3D::ParticleEmitter3D", + "type": "object" + } + ], + "objectGroups": [] + }, + { + "description": "the start max trail length of the object.", + "fullName": "Start max trail length", + "functionType": "ExpressionAndCondition", + "group": "3D particle emitter trail configuration", + "name": "TrailStartLengthMax", + "sentence": "the start max trail length", + "events": [ + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [], + "actions": [ + { + "type": { + "value": "SetReturnNumber" + }, + "parameters": [ + "TrailStartLengthMax" + ] + } + ] + } + ], + "expressionType": { + "type": "expression" + }, + "parameters": [ + { + "description": "Object", + "name": "Object", + "supplementaryInformation": "ParticleEmitter3D::ParticleEmitter3D", + "type": "object" + } + ], + "objectGroups": [] + }, + { + "fullName": "", + "functionType": "ActionWithOperator", + "getterName": "TrailStartLengthMax", + "group": "3D particle emitter trail configuration", + "name": "SetTrailStartLengthMax", + "sentence": "", + "events": [ + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [], + "actions": [ + { + "type": { + "value": "SetNumberVariable" + }, + "parameters": [ + "TrailStartLengthMax", + "=", + "Value" + ] + } + ] + }, + { + "type": "BuiltinCommonInstructions::JsCode", + "inlineCode": [ + "const object = objects[0];\r", + "const value = eventsFunctionContext.getArgument(\"Value\");\r", + "\r", + "object.__particleEmitterAdapter.setTrailStartLengthMax(value);" + ], + "parameterObjects": "Object", + "useStrict": true, + "eventsSheetExpanded": false + } + ], + "parameters": [ + { + "description": "Object", + "name": "Object", + "supplementaryInformation": "ParticleEmitter3D::ParticleEmitter3D", + "type": "object" + } + ], + "objectGroups": [] + }, + { + "description": "Check if the trail should follow the object.", + "fullName": "Trail following the object", + "functionType": "Condition", + "group": "3D particle emitter trail configuration", + "name": "IsTrailFollowingLocalOrigin", + "sentence": "The trail is following _PARAM0_ ", + "events": [ + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [ + { + "type": { + "value": "BooleanVariable" + }, + "parameters": [ + "IsTrailFollowingLocalOrigin", + "True", + "" + ] + } + ], + "actions": [ + { + "type": { + "value": "SetReturnBoolean" + }, + "parameters": [ + "True" + ] + } + ] + } + ], + "parameters": [ + { + "description": "Object", + "name": "Object", + "supplementaryInformation": "ParticleEmitter3D::ParticleEmitter3D", + "type": "object" + } + ], + "objectGroups": [] + }, + { + "description": "Change if the trail should follow the object or not.", + "fullName": "Make the trail follow", + "functionType": "Action", + "group": "3D particle emitter trail configuration", + "name": "SetIsTrailFollowingLocalOrigin", + "sentence": "Make the trail follow _PARAM0_: _PARAM1_", + "events": [ + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [ + { + "type": { + "value": "BooleanVariable" + }, + "parameters": [ + "Value", + "False", + "" + ] + } + ], + "actions": [ + { + "type": { + "value": "SetBooleanVariable" + }, + "parameters": [ + "IsTrailFollowingLocalOrigin", + "False", + "" + ] + } + ] + }, + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [ + { + "type": { + "value": "BooleanVariable" + }, + "parameters": [ + "Value", + "True", + "" + ] + } + ], + "actions": [ + { + "type": { + "value": "SetBooleanVariable" + }, + "parameters": [ + "IsTrailFollowingLocalOrigin", + "True", + "" + ] + } + ] + }, + { + "type": "BuiltinCommonInstructions::JsCode", + "inlineCode": [ + "const object = objects[0];\r", + "const value = eventsFunctionContext.getArgument(\"Value\");\r", + "\r", + "object.__particleEmitterAdapter.setTrailFollowingLocalOrigin(value);" + ], + "parameterObjects": "Object", + "useStrict": true, + "eventsSheetExpanded": false + } + ], + "parameters": [ + { + "description": "Object", + "name": "Object", + "supplementaryInformation": "ParticleEmitter3D::ParticleEmitter3D", + "type": "object" + }, + { + "defaultValue": "yes", + "description": "IsTrailFollowingLocalOrigin", + "name": "Value", + "optional": true, + "type": "yesorno" + } + ], + "objectGroups": [] + }, + { + "description": "the tail end width ratio of the object.", + "fullName": "Tail end width ratio", + "functionType": "ExpressionAndCondition", + "group": "3D particle emitter trail configuration", + "name": "TrailEndWidthRatio", + "sentence": "the tail end width ratio", + "events": [ + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [], + "actions": [ + { + "type": { + "value": "SetReturnNumber" + }, + "parameters": [ + "TrailEndWidthRatio" + ] + } + ] + } + ], + "expressionType": { + "type": "expression" + }, + "parameters": [ + { + "description": "Object", + "name": "Object", + "supplementaryInformation": "ParticleEmitter3D::ParticleEmitter3D", + "type": "object" + } + ], + "objectGroups": [] + }, + { + "fullName": "", + "functionType": "ActionWithOperator", + "getterName": "TrailEndWidthRatio", + "group": "3D particle emitter trail configuration", + "name": "SetTrailEndWidthRatio", + "sentence": "", + "events": [ + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [], + "actions": [ + { + "type": { + "value": "SetNumberVariable" + }, + "parameters": [ + "TrailEndWidthRatio", + "=", + "Value" + ] + } + ] + }, + { + "type": "BuiltinCommonInstructions::JsCode", + "inlineCode": [ + "const object = objects[0];\r", + "const value = eventsFunctionContext.getArgument(\"Value\");\r", + "\r", + "object.__particleEmitterAdapter.setTrailEndWidthRatio(value);" + ], + "parameterObjects": "Object", + "useStrict": true, + "eventsSheetExpanded": false + } + ], + "parameters": [ + { + "description": "Object", + "name": "Object", + "supplementaryInformation": "ParticleEmitter3D::ParticleEmitter3D", + "type": "object" + } + ], + "objectGroups": [] + }, + { + "description": "the render mode of the object.", + "fullName": "Render mode", + "functionType": "ExpressionAndCondition", + "group": "3D particle emitter configuration", + "name": "RenderMode", + "private": true, + "sentence": "the render mode", + "events": [ + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [], + "actions": [ + { + "type": { + "value": "SetReturnString" + }, + "parameters": [ + "RenderMode" + ] + } + ] + } + ], + "expressionType": { + "supplementaryInformation": "[\"Billboard\",\"Trail\"]", + "type": "stringWithSelector" + }, + "parameters": [ + { + "description": "Object", + "name": "Object", + "supplementaryInformation": "ParticleEmitter3D::ParticleEmitter3D", + "type": "object" + } + ], + "objectGroups": [] + }, + { + "fullName": "", + "functionType": "ActionWithOperator", + "getterName": "RenderMode", + "group": "3D particle emitter configuration", + "name": "SetRenderMode", + "private": true, + "sentence": "", + "events": [ + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [], + "actions": [ + { + "type": { + "value": "SetStringVariable" + }, + "parameters": [ + "RenderMode", + "=", + "Value" + ] + } + ] + }, + { + "type": "BuiltinCommonInstructions::JsCode", + "inlineCode": [ + "const object = objects[0];\r", + "const value = eventsFunctionContext.getArgument(\"Value\");\r", + "\r", + "object.__particleEmitterAdapter.setRenderMode(value);" + ], + "parameterObjects": "Object", + "useStrict": true, + "eventsSheetExpanded": false + }, + { + "type": "BuiltinCommonInstructions::Comment", + "color": { + "b": 109, + "g": 230, + "r": 255, + "textB": 0, + "textG": 0, + "textR": 0 + }, + "comment": "Update the trail properties because the RenderMode setter reset them" + }, + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [ + { + "type": { + "value": "StringVariable" + }, + "parameters": [ + "RenderMode", + "=", + "\"Trail\"" + ] + } + ], + "actions": [ + { + "type": { + "value": "ParticleEmitter3D::ParticleEmitter3D::SetTrailStartLengthMin" + }, + "parameters": [ + "Object", + "=", + "TrailStartLengthMin", + "" + ] + }, + { + "type": { + "value": "ParticleEmitter3D::ParticleEmitter3D::SetTrailStartLengthMax" + }, + "parameters": [ + "Object", + "=", + "TrailStartLengthMax", + "" + ] + } + ] + } + ], + "parameters": [ + { + "description": "Object", + "name": "Object", + "supplementaryInformation": "ParticleEmitter3D::ParticleEmitter3D", + "type": "object" + } + ], + "objectGroups": [] + } + ], + "eventsFunctionsFolderStructure": { + "folderName": "__ROOT", + "children": [ + { + "functionName": "onCreated" + }, + { + "functionName": "onDestroy" + }, + { + "functionName": "onHotReloading" + }, + { + "functionName": "UpdateFromProperties" + }, + { + "functionName": "UpdateHelper" + }, + { + "functionName": "UpdateImage" + }, + { + "functionName": "doStepPostEvents" + }, + { + "functionName": "RegisterInLayer" + }, + { + "functionName": "Delete" + }, + { + "functionName": "HasEnded" + }, + { + "functionName": "Restart" + }, + { + "folderName": "Position", + "children": [ + { + "functionName": "Z" + }, + { + "functionName": "SetZ" + } + ] + }, + { + "folderName": "Angle", + "children": [ + { + "functionName": "RotationX" + }, + { + "functionName": "SetRotationX" + }, + { + "functionName": "RotationY" + }, + { + "functionName": "SetRotationY" + } + ] + }, + { + "folderName": "3D particle emitter color configuration", + "children": [ + { + "functionName": "StartColor" + }, + { + "functionName": "SetStartColor" + }, + { + "functionName": "EndColor" + }, + { + "functionName": "SetEndColor" + }, + { + "functionName": "StartOpacity" + }, + { + "functionName": "SetStartOpacity" + }, + { + "functionName": "EndOpacity" + }, + { + "functionName": "SetEndOpacity" + }, + { + "functionName": "Blending" + }, + { + "functionName": "SetBlending" + } + ] + }, + { + "folderName": "3D particle emitter configuration", + "children": [ + { + "functionName": "Flow" + }, + { + "functionName": "SetFlow" + }, + { + "functionName": "StartSizeMin" + }, + { + "functionName": "SetStartSizeMin" + }, + { + "functionName": "StartSizeMax" + }, + { + "functionName": "SetStartSizeMax" + }, + { + "functionName": "EndScale" + }, + { + "functionName": "SetEndScale" + }, + { + "functionName": "LifespanMin" + }, + { + "functionName": "SetLifespanMin" + }, + { + "functionName": "LifespanMax" + }, + { + "functionName": "SetLifespanMax" + }, + { + "functionName": "Duration" + }, + { + "functionName": "SetDuration" + }, + { + "functionName": "SpayConeAngle" + }, + { + "functionName": "SetSpayConeAngle" + }, + { + "functionName": "ShouldAutodestruct" + }, + { + "functionName": "SetShouldAutodestruct" + }, + { + "functionName": "RenderMode" + }, + { + "functionName": "SetRenderMode" + } + ] + }, + { + "folderName": "3D particle emitter speed configuration", + "children": [ + { + "functionName": "StartSpeedMin" + }, + { + "functionName": "SetStartSpeedMin" + }, + { + "functionName": "StartSpeedMax" + }, + { + "functionName": "SetStartSpeedMax" + }, + { + "functionName": "AreParticlesRelative" + }, + { + "functionName": "SetAreParticlesRelative" + }, + { + "functionName": "GravityTop" + }, + { + "functionName": "SetGravityTop" + }, + { + "functionName": "Gravity" + }, + { + "functionName": "SetGravity" + } + ] + }, + { + "folderName": "3D particle emitter trail configuration", + "children": [ + { + "functionName": "TrailStartLengthMin" + }, + { + "functionName": "SetTrailStartLengthMin" + }, + { + "functionName": "TrailStartLengthMax" + }, + { + "functionName": "SetTrailStartLengthMax" + }, + { + "functionName": "IsTrailFollowingLocalOrigin" + }, + { + "functionName": "SetIsTrailFollowingLocalOrigin" + }, + { + "functionName": "TrailEndWidthRatio" + }, + { + "functionName": "SetTrailEndWidthRatio" + } + ] + } + ] + }, + "propertyDescriptors": [ + { + "value": "255;0;0", + "type": "Color", + "label": "Start color", + "group": "Color", + "name": "StartColor" + }, + { + "value": "255;255;0", + "type": "Color", + "label": "End color", + "group": "Color", + "name": "EndColor" + }, + { + "value": "255", + "type": "Number", + "unit": "Dimensionless", + "label": "Start opacity", + "group": "Color", + "name": "StartOpacity" + }, + { + "value": "0", + "type": "Number", + "unit": "Dimensionless", + "label": "End opacity", + "group": "Color", + "name": "EndOpacity" + }, + { + "value": "50", + "type": "Number", + "label": "Flow of particles (particles per second)", + "name": "Flow" + }, + { + "value": "10", + "type": "Number", + "unit": "Pixel", + "label": "Start min size", + "group": "Size", + "name": "StartSizeMin" + }, + { + "value": "20", + "type": "Number", + "unit": "Pixel", + "label": "Start max size", + "group": "Size", + "name": "StartSizeMax" + }, + { + "value": "0", + "type": "Number", + "unit": "Dimensionless", + "label": "End scale", + "group": "Size", + "name": "EndScale" + }, + { + "value": "100", + "type": "Number", + "unit": "PixelSpeed", + "label": "Start min speed", + "group": "Speed", + "name": "StartSpeedMin" + }, + { + "value": "100", + "type": "Number", + "unit": "PixelSpeed", + "label": "Start max speed", + "group": "Speed", + "name": "StartSpeedMax" + }, + { + "value": "1", + "type": "Number", + "unit": "Second", + "label": "Min lifespan", + "name": "LifespanMin" + }, + { + "value": "2", + "type": "Number", + "unit": "Second", + "label": "Max lifespan", + "name": "LifespanMax" + }, + { + "value": "5", + "type": "Number", + "unit": "Second", + "label": "Emission duration", + "name": "Duration" + }, + { + "value": "", + "type": "Boolean", + "label": "Particles move with the emitter", + "group": "Speed", + "name": "AreParticlesRelative" + }, + { + "value": "30", + "type": "Number", + "unit": "DegreeAngle", + "label": "Spay cone angle", + "group": "Position", + "name": "SpayConeAngle" + }, + { + "value": "Additive", + "type": "Choice", + "label": "Blending", + "group": "Color", + "choices": [ + { + "label": "Normal", + "value": "Normal" + }, + { + "label": "Additive", + "value": "Additive" + }, + { + "label": "Subtractive", + "value": "Subtractive" + }, + { + "label": "Multiply", + "value": "Multiply" + }, + { + "label": "None", + "value": "None" + } + ], + "name": "Blending" + }, + { + "value": "Y-", + "type": "Choice", + "label": "Gravity top", + "group": "Speed", + "choices": [ + { + "label": "Y-", + "value": "Y-" + }, + { + "label": "Z+", + "value": "Z+" + } + ], + "name": "GravityTop" + }, + { + "value": "0", + "type": "Number", + "unit": "PixelAcceleration", + "label": "Gravity", + "group": "Speed", + "name": "Gravity" + }, + { + "value": "true", + "type": "Boolean", + "label": "Delete when emission ends", + "name": "ShouldAutodestruct" + }, + { + "value": "Center-center", + "type": "String", + "label": "", + "description": "Only used by the scene editor.", + "hidden": true, + "name": "ParentOrigin" + }, + { + "value": "0", + "type": "Number", + "unit": "Pixel", + "label": "Z (elevation)", + "description": "Deprecated", + "group": "Position", + "hidden": true, + "name": "Z" + }, + { + "value": "0", + "type": "Number", + "unit": "DegreeAngle", + "label": "Rotation on X axis", + "group": "Position", + "hidden": true, + "name": "RotationX" + }, + { + "value": "0", + "type": "Number", + "unit": "DegreeAngle", + "label": "Rotation on Y axis", + "group": "Position", + "hidden": true, + "name": "RotationY" + }, + { + "value": "120", + "type": "Number", + "unit": "Pixel", + "label": "Start min length", + "group": "Trail", + "advanced": true, + "name": "TrailStartLengthMin" + }, + { + "value": "120", + "type": "Number", + "unit": "Pixel", + "label": "Start max length", + "group": "Trail", + "advanced": true, + "name": "TrailStartLengthMax" + }, + { + "value": "Billboard", + "type": "Choice", + "label": "Render mode", + "choices": [ + { + "label": "Image facing the camera", + "value": "Billboard" + }, + { + "label": "Image following a trail", + "value": "Trail" + } + ], + "advanced": true, + "name": "RenderMode" + }, + { + "value": "", + "type": "Boolean", + "label": "Follow the object", + "group": "Trail", + "advanced": true, + "name": "IsTrailFollowingLocalOrigin" + }, + { + "value": "0", + "type": "Number", + "unit": "Dimensionless", + "label": "Tail end width ratio", + "group": "Trail", + "advanced": true, + "name": "TrailEndWidthRatio" + } + ], + "propertiesFolderStructure": { + "folderName": "__ROOT", + "children": [ + { + "propertyName": "Flow" + }, + { + "propertyName": "LifespanMin" + }, + { + "propertyName": "LifespanMax" + }, + { + "propertyName": "Duration" + }, + { + "propertyName": "ShouldAutodestruct" + }, + { + "propertyName": "RenderMode" + }, + { + "folderName": "Color", + "children": [ + { + "propertyName": "StartColor" + }, + { + "propertyName": "EndColor" + }, + { + "propertyName": "StartOpacity" + }, + { + "propertyName": "EndOpacity" + }, + { + "propertyName": "Blending" + } + ] + }, + { + "folderName": "Size", + "children": [ + { + "propertyName": "StartSizeMin" + }, + { + "propertyName": "StartSizeMax" + }, + { + "propertyName": "EndScale" + } + ] + }, + { + "folderName": "Trail", + "children": [ + { + "propertyName": "TrailStartLengthMin" + }, + { + "propertyName": "TrailStartLengthMax" + }, + { + "propertyName": "TrailEndWidthRatio" + }, + { + "propertyName": "IsTrailFollowingLocalOrigin" + } + ] + }, + { + "folderName": "Speed", + "children": [ + { + "propertyName": "StartSpeedMin" + }, + { + "propertyName": "StartSpeedMax" + }, + { + "propertyName": "AreParticlesRelative" + }, + { + "propertyName": "GravityTop" + }, + { + "propertyName": "Gravity" + } + ] + }, + { + "folderName": "Position", + "children": [ + { + "propertyName": "SpayConeAngle" + }, + { + "propertyName": "Z" + }, + { + "propertyName": "RotationX" + }, + { + "propertyName": "RotationY" + } + ] + }, + { + "propertyName": "ParentOrigin" + } + ] + }, + "variants": [] + } + ] + }, + { + "author": "", + "category": "Visual effect", + "dimension": "3D", + "extensionNamespace": "", + "fullName": "3D object shake", + "gdevelopVersion": ">=5.5.222", + "helpPath": "", + "iconUrl": "data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iVVRGLTgiPz48IURPQ1RZUEUgc3ZnIFBVQkxJQyAiLS8vVzNDLy9EVEQgU1ZHIDEuMS8vRU4iICJodHRwOi8vd3d3LnczLm9yZy9HcmFwaGljcy9TVkcvMS4xL0RURC9zdmcxMS5kdGQiPjxzdmcgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIiB4bWxuczp4bGluaz0iaHR0cDovL3d3dy53My5vcmcvMTk5OS94bGluayIgdmVyc2lvbj0iMS4xIiBpZD0ibWRpLXZlY3Rvci1kaWZmZXJlbmNlLWFiIiB3aWR0aD0iMjQiIGhlaWdodD0iMjQiIHZpZXdCb3g9IjAgMCAyNCAyNCI+PHBhdGggZD0iTTMsMUMxLjg5LDEgMSwxLjg5IDEsM1Y1SDNWM0g1VjFIM003LDFWM0gxMFYxSDdNMTIsMVYzSDE0VjVIMTZWM0MxNiwxLjg5IDE1LjExLDEgMTQsMUgxMk0xLDdWMTBIM1Y3SDFNMTQsN0MxNCw3IDE0LDExLjY3IDE0LDE0QzExLjY3LDE0IDcsMTQgNywxNEM3LDE0IDcsMTggNywyMEM3LDIxLjExIDcuODksMjIgOSwyMkgyMEMyMS4xMSwyMiAyMiwyMS4xMSAyMiwyMFY5QzIyLDcuODkgMjEuMTEsNyAyMCw3QzE4LDcgMTQsNyAxNCw3TTE2LDlIMjBWMjBIOVYxNkgxNEMxNS4xMSwxNiAxNiwxNS4xMSAxNiwxNFY5TTEsMTJWMTRDMSwxNS4xMSAxLjg5LDE2IDMsMTZINVYxNEgzVjEySDFaIiAvPjwvc3ZnPg==", + "name": "ShakeObject3D", + "previewIconUrl": "https://resources.gdevelop-app.com/assets/Icons/vector-difference-ab.svg", + "shortDescription": "Shake 3D objects.", + "version": "2.1.1", + "description": [ + "Shake 3D objects with translation and rotation.", + "", + "The 3D racing game example uses this extension ([open the project online](https://editor.gdevelop.io/?project=example://3d-racing-game))." + ], + "origin": { + "identifier": "ShakeObject3D", + "name": "gdevelop-extension-store" + }, + "tags": [ + "3d", + "shaking", + "effect", + "shake", + "translate", + "rotate" + ], + "authorIds": [ + "IWykYNRvhCZBN3vEgKEbBPOR3Oc2" + ], + "changelog": [ + { + "version": "2.0.0", + "breaking": "- The behavior for 3D box has been removed. The other behavior can be used for both models and boxes." + } + ], + "dependencies": [], + "globalVariables": [], + "sceneVariables": [], + "eventsFunctions": [ + { + "fullName": "", + "functionType": "Action", + "name": "onFirstSceneLoaded", + "sentence": "", + "events": [ + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [], + "actions": [ + { + "type": { + "value": "ShakeObject3D::DefineHelperClasses" + }, + "parameters": [ + "", + "" + ] + } + ] + } + ], + "parameters": [], + "objectGroups": [] + }, + { + "description": "Define helper classes JavaScript code.", + "fullName": "Define helper classes", + "functionType": "Action", + "name": "DefineHelperClasses", + "private": true, + "sentence": "Define helper classes JavaScript code", + "events": [ + { + "type": "BuiltinCommonInstructions::JsCode", + "inlineCode": [ + "if (gdjs._shakeObjectExtension) {", + " return;", + "}", + "", + "/** Noise generator manager. */", + "class NoiseManager {", + " /**", + " * Create the manager of noise generators.", + " */", + " constructor() {", + " this.seed = gdjs.randomInRange(1, Number.MAX_SAFE_INTEGER);", + " /** @type {Map} */", + " this.generators = new Map();", + " }", + "", + " /**", + " * @param name {string}", + " * @return {NoiseGenerator}", + " */", + " getGenerator(name) {", + " let generator = this.generators.get(name);", + " if (!generator) {", + " generator = new NoiseGenerator(name + this.seed);", + " this.generators.set(name, generator);", + " }", + " return generator;", + " }", + "", + " /**", + " * @param seed {number}", + " */", + " setSeed(seed) {", + " this.seed = seed;", + " this.generators.forEach(generator => generator.setSeed(name + this.seed));", + " }", + "", + " /**", + " * @param name {string}", + " */", + " deleteGenerator(name) {", + " this.generators.delete(name);", + " }", + "", + " /**", + " */", + " deleteAllGenerators() {", + " this.generators.clear();", + " }", + "}", + "", + "/** Noise generator with octaves. */", + "class NoiseGenerator {", + " /**", + " * Create a noise generator with a seed.", + " * @param seed {string}", + " */", + " constructor(seed) {", + " this.simplexNoise = new SimplexNoise(seed);", + " this.frequency = 1;", + " this.octaves = 1;", + " this.persistence = 0.5;", + " this.lacunarity = 2;", + " this.xLoopPeriod = 0;", + " this.yLoopPeriod = 0;", + " }", + "", + " /**", + " * @param seed {string}", + " */", + " setSeed(seed) {", + " this.simplexNoise = new SimplexNoise(seed);", + " }", + "", + " /**", + " * @param x {float}", + " * @param y {float}", + " * @param z {float} optionnal", + " * @param w {float} optionnal", + " * @return {float}", + " */", + " noise(x, y, z, w) {", + " if (this.xLoopPeriod && this.yLoopPeriod) {", + " const circleRatioX = 2 * Math.PI / this.xLoopPeriod;", + " const circleRatioY = 2 * Math.PI / this.yLoopPeriod;", + " const angleX = circleRatioX * x;", + " const angleY = circleRatioY * y;", + " x = Math.cos(angleX) / circleRatioX;", + " y = Math.sin(angleX) / circleRatioX;", + " z = Math.cos(angleY) / circleRatioY;", + " w = Math.sin(angleY) / circleRatioY;", + " }", + " else if (this.xLoopPeriod) {", + " const circleRatio = 2 * Math.PI / this.xLoopPeriod;", + " const angleX = circleRatio * x;", + " w = z;", + " z = y;", + " x = Math.cos(angleX) / circleRatio;", + " y = Math.sin(angleX) / circleRatio;", + " }", + " else if (this.yLoopPeriod) {", + " const circleRatio = 2 * Math.PI / this.xLoopPeriod;", + " const angleX = circleRatio * x;", + " w = z;", + " // Make the circle perimeter equals to the looping period", + " // to keep the same perceived frequency with or without looping.", + " y = Math.cos(angleX) / circleRatio;", + " z = Math.sin(angleX) / circleRatio;", + " }", + " let noiseFunction = this.simplexNoise.noise4D.bind(this.simplexNoise);", + " if (z === undefined) {", + " noiseFunction = this.simplexNoise.noise2D.bind(this.simplexNoise);", + " }", + " else if (w === undefined) {", + " noiseFunction = this.simplexNoise.noise3D.bind(this.simplexNoise);", + " }", + " let frequency = this.frequency;", + " let noiseSum = 0;", + " let amplitudeSum = 0;", + " let amplitude = 1;", + " for (let i = 0; i < this.octaves; i++) {", + " noiseSum += noiseFunction(x * frequency, y * frequency, z * frequency, w * frequency) * amplitude;", + " amplitudeSum += Math.abs(amplitude);", + " amplitude *= this.persistence;", + " frequency *= this.lacunarity;", + " }", + " return noiseSum / amplitudeSum;", + " }", + "}", + "", + "/*", + "A fast javascript implementation of simplex noise by Jonas Wagner", + "https://github.com/jwagner/simplex-noise.js", + "", + "Based on a speed-improved simplex noise algorithm for 2D, 3D and 4D in Java.", + "Which is based on example code by Stefan Gustavson (stegu@itn.liu.se).", + "With Optimisations by Peter Eastman (peastman@drizzle.stanford.edu).", + "Better rank ordering method by Stefan Gustavson in 2012.", + "", + " Copyright (c) 2021 Jonas Wagner", + "", + " Permission is hereby granted, free of charge, to any person obtaining a copy", + " of this software and associated documentation files (the \"Software\"), to deal", + " in the Software without restriction, including without limitation the rights", + " to use, copy, modify, merge, publish, distribute, sublicense, and/or sell", + " copies of the Software, and to permit persons to whom the Software is", + " furnished to do so, subject to the following conditions:", + "", + " The above copyright notice and this permission notice shall be included in all", + " copies or substantial portions of the Software.", + "", + " THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR", + " IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,", + " FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE", + " AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER", + " LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,", + " OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE", + " SOFTWARE.", + " */", + "", + "const F2 = 0.5 * (Math.sqrt(3.0) - 1.0);", + "const G2 = (3.0 - Math.sqrt(3.0)) / 6.0;", + "const F3 = 1.0 / 3.0;", + "const G3 = 1.0 / 6.0;", + "const F4 = (Math.sqrt(5.0) - 1.0) / 4.0;", + "const G4 = (5.0 - Math.sqrt(5.0)) / 20.0;", + "const grad3 = new Float32Array([1, 1, 0,", + " -1, 1, 0,", + " 1, -1, 0,", + " -1, -1, 0,", + " 1, 0, 1,", + " -1, 0, 1,", + " 1, 0, -1,", + " -1, 0, -1,", + " 0, 1, 1,", + " 0, -1, 1,", + " 0, 1, -1,", + " 0, -1, -1]);", + "const grad4 = new Float32Array([0, 1, 1, 1, 0, 1, 1, -1, 0, 1, -1, 1, 0, 1, -1, -1,", + " 0, -1, 1, 1, 0, -1, 1, -1, 0, -1, -1, 1, 0, -1, -1, -1,", + " 1, 0, 1, 1, 1, 0, 1, -1, 1, 0, -1, 1, 1, 0, -1, -1,", + " -1, 0, 1, 1, -1, 0, 1, -1, -1, 0, -1, 1, -1, 0, -1, -1,", + " 1, 1, 0, 1, 1, 1, 0, -1, 1, -1, 0, 1, 1, -1, 0, -1,", + " -1, 1, 0, 1, -1, 1, 0, -1, -1, -1, 0, 1, -1, -1, 0, -1,", + " 1, 1, 1, 0, 1, 1, -1, 0, 1, -1, 1, 0, 1, -1, -1, 0,", + " -1, 1, 1, 0, -1, 1, -1, 0, -1, -1, 1, 0, -1, -1, -1, 0]);", + "", + "", + "/**", + " * Builds a random permutation table.", + " * This is exported only for (internal) testing purposes.", + " * Do not rely on this export.", + " * @param {() => number} random", + " * @private", + " */", + "function buildPermutationTable(random) {", + " const p = new Uint8Array(256);", + " for (let i = 0; i < 256; i++) {", + " p[i] = i;", + " }", + " for (let i = 0; i < 255; i++) {", + " const r = i + ~~(random() * (256 - i));", + " const aux = p[i];", + " p[i] = p[r];", + " p[r] = aux;", + " }", + " return p;", + "}", + "", + "/*", + "The ALEA PRNG and masher code used by simplex-noise.js", + "is based on code by Johannes Baagøe, modified by Jonas Wagner.", + "See alea.md for the full license.", + "@param {string|number} seed", + "*/", + "function alea(seed) {", + " let s0 = 0;", + " let s1 = 0;", + " let s2 = 0;", + " let c = 1;", + " const mash = masher();", + " s0 = mash(' ');", + " s1 = mash(' ');", + " s2 = mash(' ');", + " s0 -= mash(seed);", + " if (s0 < 0) {", + " s0 += 1;", + " }", + " s1 -= mash(seed);", + " if (s1 < 0) {", + " s1 += 1;", + " }", + " s2 -= mash(seed);", + " if (s2 < 0) {", + " s2 += 1;", + " }", + " return function () {", + " const t = 2091639 * s0 + c * 2.3283064365386963e-10; // 2^-32", + " s0 = s1;", + " s1 = s2;", + " return s2 = t - (c = t | 0);", + " };", + "}", + "", + "function masher() {", + " let n = 0xefc8249d;", + " return function (data) {", + " data = data.toString();", + " for (let i = 0; i < data.length; i++) {", + " n += data.charCodeAt(i);", + " let h = 0.02519603282416938 * n;", + " n = h >>> 0;", + " h -= n;", + " h *= n;", + " n = h >>> 0;", + " h -= n;", + " n += h * 0x100000000; // 2^32", + " }", + " return (n >>> 0) * 2.3283064365386963e-10; // 2^-32", + " };", + "}", + "", + "/** Deterministic simplex noise generator suitable for 2D, 3D and 4D spaces. */", + "class SimplexNoise {", + " /**", + " * Creates a new `SimplexNoise` instance.", + " * This involves some setup. You can save a few cpu cycles by reusing the same instance.", + " * @param {(() => number)|string|number} randomOrSeed A random number generator or a seed (string|number).", + " * Defaults to Math.random (random irreproducible initialization).", + " */", + " constructor(randomOrSeed) {", + " if (randomOrSeed === void 0) { randomOrSeed = Math.random; }", + " const random = typeof randomOrSeed == 'function' ? randomOrSeed : alea(randomOrSeed);", + " this.p = buildPermutationTable(random);", + " this.perm = new Uint8Array(512);", + " this.permMod12 = new Uint8Array(512);", + " for (let i = 0; i < 512; i++) {", + " this.perm[i] = this.p[i & 255];", + " this.permMod12[i] = this.perm[i] % 12;", + " }", + " }", + "", + " /**", + " * Samples the noise field in 2 dimensions", + " * @param {number} x", + " * @param {number} y", + " * @returns a number in the interval [-1, 1]", + " */", + " noise2D(x, y) {", + " const permMod12 = this.permMod12;", + " const perm = this.perm;", + " let n0 = 0; // Noise contributions from the three corners", + " let n1 = 0;", + " let n2 = 0;", + " // Skew the input space to determine which simplex cell we're in", + " const s = (x + y) * F2; // Hairy factor for 2D", + " const i = Math.floor(x + s);", + " const j = Math.floor(y + s);", + " const t = (i + j) * G2;", + " const X0 = i - t; // Unskew the cell origin back to (x,y) space", + " const Y0 = j - t;", + " const x0 = x - X0; // The x,y distances from the cell origin", + " const y0 = y - Y0;", + " // For the 2D case, the simplex shape is an equilateral triangle.", + " // Determine which simplex we are in.", + " let i1, j1; // Offsets for second (middle) corner of simplex in (i,j) coords", + " if (x0 > y0) {", + " i1 = 1;", + " j1 = 0;", + " } // lower triangle, XY order: (0,0)->(1,0)->(1,1)", + " else {", + " i1 = 0;", + " j1 = 1;", + " } // upper triangle, YX order: (0,0)->(0,1)->(1,1)", + " // A step of (1,0) in (i,j) means a step of (1-c,-c) in (x,y), and", + " // a step of (0,1) in (i,j) means a step of (-c,1-c) in (x,y), where", + " // c = (3-sqrt(3))/6", + " const x1 = x0 - i1 + G2; // Offsets for middle corner in (x,y) unskewed coords", + " const y1 = y0 - j1 + G2;", + " const x2 = x0 - 1.0 + 2.0 * G2; // Offsets for last corner in (x,y) unskewed coords", + " const y2 = y0 - 1.0 + 2.0 * G2;", + " // Work out the hashed gradient indices of the three simplex corners", + " const ii = i & 255;", + " const jj = j & 255;", + " // Calculate the contribution from the three corners", + " let t0 = 0.5 - x0 * x0 - y0 * y0;", + " if (t0 >= 0) {", + " const gi0 = permMod12[ii + perm[jj]] * 3;", + " t0 *= t0;", + " n0 = t0 * t0 * (grad3[gi0] * x0 + grad3[gi0 + 1] * y0); // (x,y) of grad3 used for 2D gradient", + " }", + " let t1 = 0.5 - x1 * x1 - y1 * y1;", + " if (t1 >= 0) {", + " const gi1 = permMod12[ii + i1 + perm[jj + j1]] * 3;", + " t1 *= t1;", + " n1 = t1 * t1 * (grad3[gi1] * x1 + grad3[gi1 + 1] * y1);", + " }", + " let t2 = 0.5 - x2 * x2 - y2 * y2;", + " if (t2 >= 0) {", + " const gi2 = permMod12[ii + 1 + perm[jj + 1]] * 3;", + " t2 *= t2;", + " n2 = t2 * t2 * (grad3[gi2] * x2 + grad3[gi2 + 1] * y2);", + " }", + " // Add contributions from each corner to get the final noise value.", + " // The result is scaled to return values in the interval [-1,1].", + " return 70.0 * (n0 + n1 + n2);", + " }", + "", + " /**", + " * Samples the noise field in 3 dimensions", + " * @param {number} x", + " * @param {number} y", + " * @param {number} z", + " * @returns a number in the interval [-1, 1]", + " */", + " noise3D(x, y, z) {", + " const permMod12 = this.permMod12;", + " const perm = this.perm;", + " let n0, n1, n2, n3; // Noise contributions from the four corners", + " // Skew the input space to determine which simplex cell we're in", + " const s = (x + y + z) * F3; // Very nice and simple skew factor for 3D", + " const i = Math.floor(x + s);", + " const j = Math.floor(y + s);", + " const k = Math.floor(z + s);", + " const t = (i + j + k) * G3;", + " const X0 = i - t; // Unskew the cell origin back to (x,y,z) space", + " const Y0 = j - t;", + " const Z0 = k - t;", + " const x0 = x - X0; // The x,y,z distances from the cell origin", + " const y0 = y - Y0;", + " const z0 = z - Z0;", + " // For the 3D case, the simplex shape is a slightly irregular tetrahedron.", + " // Determine which simplex we are in.", + " let i1, j1, k1; // Offsets for second corner of simplex in (i,j,k) coords", + " let i2, j2, k2; // Offsets for third corner of simplex in (i,j,k) coords", + " if (x0 >= y0) {", + " if (y0 >= z0) {", + " i1 = 1;", + " j1 = 0;", + " k1 = 0;", + " i2 = 1;", + " j2 = 1;", + " k2 = 0;", + " } // X Y Z order", + " else if (x0 >= z0) {", + " i1 = 1;", + " j1 = 0;", + " k1 = 0;", + " i2 = 1;", + " j2 = 0;", + " k2 = 1;", + " } // X Z Y order", + " else {", + " i1 = 0;", + " j1 = 0;", + " k1 = 1;", + " i2 = 1;", + " j2 = 0;", + " k2 = 1;", + " } // Z X Y order", + " }", + " else { // x0 y0)", + " rankx++;", + " else", + " ranky++;", + " if (x0 > z0)", + " rankx++;", + " else", + " rankz++;", + " if (x0 > w0)", + " rankx++;", + " else", + " rankw++;", + " if (y0 > z0)", + " ranky++;", + " else", + " rankz++;", + " if (y0 > w0)", + " ranky++;", + " else", + " rankw++;", + " if (z0 > w0)", + " rankz++;", + " else", + " rankw++;", + " // simplex[c] is a 4-vector with the numbers 0, 1, 2 and 3 in some order.", + " // Many values of c will never occur, since e.g. x>y>z>w makes x= 3 ? 1 : 0;", + " const j1 = ranky >= 3 ? 1 : 0;", + " const k1 = rankz >= 3 ? 1 : 0;", + " const l1 = rankw >= 3 ? 1 : 0;", + " // The integer offsets for the third simplex corner", + " const i2 = rankx >= 2 ? 1 : 0;", + " const j2 = ranky >= 2 ? 1 : 0;", + " const k2 = rankz >= 2 ? 1 : 0;", + " const l2 = rankw >= 2 ? 1 : 0;", + " // The integer offsets for the fourth simplex corner", + " const i3 = rankx >= 1 ? 1 : 0;", + " const j3 = ranky >= 1 ? 1 : 0;", + " const k3 = rankz >= 1 ? 1 : 0;", + " const l3 = rankw >= 1 ? 1 : 0;", + " // The fifth corner has all coordinate offsets = 1, so no need to compute that.", + " const x1 = x0 - i1 + G4; // Offsets for second corner in (x,y,z,w) coords", + " const y1 = y0 - j1 + G4;", + " const z1 = z0 - k1 + G4;", + " const w1 = w0 - l1 + G4;", + " const x2 = x0 - i2 + 2.0 * G4; // Offsets for third corner in (x,y,z,w) coords", + " const y2 = y0 - j2 + 2.0 * G4;", + " const z2 = z0 - k2 + 2.0 * G4;", + " const w2 = w0 - l2 + 2.0 * G4;", + " const x3 = x0 - i3 + 3.0 * G4; // Offsets for fourth corner in (x,y,z,w) coords", + " const y3 = y0 - j3 + 3.0 * G4;", + " const z3 = z0 - k3 + 3.0 * G4;", + " const w3 = w0 - l3 + 3.0 * G4;", + " const x4 = x0 - 1.0 + 4.0 * G4; // Offsets for last corner in (x,y,z,w) coords", + " const y4 = y0 - 1.0 + 4.0 * G4;", + " const z4 = z0 - 1.0 + 4.0 * G4;", + " const w4 = w0 - 1.0 + 4.0 * G4;", + " // Work out the hashed gradient indices of the five simplex corners", + " const ii = i & 255;", + " const jj = j & 255;", + " const kk = k & 255;", + " const ll = l & 255;", + " // Calculate the contribution from the five corners", + " let t0 = 0.6 - x0 * x0 - y0 * y0 - z0 * z0 - w0 * w0;", + " if (t0 < 0)", + " n0 = 0.0;", + " else {", + " const gi0 = (perm[ii + perm[jj + perm[kk + perm[ll]]]] % 32) * 4;", + " t0 *= t0;", + " n0 = t0 * t0 * (grad4[gi0] * x0 + grad4[gi0 + 1] * y0 + grad4[gi0 + 2] * z0 + grad4[gi0 + 3] * w0);", + " }", + " let t1 = 0.6 - x1 * x1 - y1 * y1 - z1 * z1 - w1 * w1;", + " if (t1 < 0)", + " n1 = 0.0;", + " else {", + " const gi1 = (perm[ii + i1 + perm[jj + j1 + perm[kk + k1 + perm[ll + l1]]]] % 32) * 4;", + " t1 *= t1;", + " n1 = t1 * t1 * (grad4[gi1] * x1 + grad4[gi1 + 1] * y1 + grad4[gi1 + 2] * z1 + grad4[gi1 + 3] * w1);", + " }", + " let t2 = 0.6 - x2 * x2 - y2 * y2 - z2 * z2 - w2 * w2;", + " if (t2 < 0)", + " n2 = 0.0;", + " else {", + " const gi2 = (perm[ii + i2 + perm[jj + j2 + perm[kk + k2 + perm[ll + l2]]]] % 32) * 4;", + " t2 *= t2;", + " n2 = t2 * t2 * (grad4[gi2] * x2 + grad4[gi2 + 1] * y2 + grad4[gi2 + 2] * z2 + grad4[gi2 + 3] * w2);", + " }", + " let t3 = 0.6 - x3 * x3 - y3 * y3 - z3 * z3 - w3 * w3;", + " if (t3 < 0)", + " n3 = 0.0;", + " else {", + " const gi3 = (perm[ii + i3 + perm[jj + j3 + perm[kk + k3 + perm[ll + l3]]]] % 32) * 4;", + " t3 *= t3;", + " n3 = t3 * t3 * (grad4[gi3] * x3 + grad4[gi3 + 1] * y3 + grad4[gi3 + 2] * z3 + grad4[gi3 + 3] * w3);", + " }", + " let t4 = 0.6 - x4 * x4 - y4 * y4 - z4 * z4 - w4 * w4;", + " if (t4 < 0)", + " n4 = 0.0;", + " else {", + " const gi4 = (perm[ii + 1 + perm[jj + 1 + perm[kk + 1 + perm[ll + 1]]]] % 32) * 4;", + " t4 *= t4;", + " n4 = t4 * t4 * (grad4[gi4] * x4 + grad4[gi4 + 1] * y4 + grad4[gi4 + 2] * z4 + grad4[gi4 + 3] * w4);", + " }", + " // Sum up and scale the result to cover the range [-1,1]", + " return 27.0 * (n0 + n1 + n2 + n3 + n4);", + " };", + "}", + "", + "gdjs._shakeObjectExtension = {", + " noiseManager: new NoiseManager(),", + "};", + "" + ], + "parameterObjects": "", + "useStrict": true, + "eventsSheetExpanded": true + } + ], + "parameters": [], + "objectGroups": [] + }, + { + "description": "Generate a number from 2 dimensional simplex noise.", + "fullName": "2D noise", + "functionType": "Expression", + "name": "Noise2d", + "private": true, + "sentence": "", + "events": [ + { + "type": "BuiltinCommonInstructions::JsCode", + "inlineCode": [ + "const name = eventsFunctionContext.getArgument(\"Name\");\r", + "const x = eventsFunctionContext.getArgument(\"X\");\r", + "const y = eventsFunctionContext.getArgument(\"Y\");\r", + "\r", + "eventsFunctionContext.returnValue = gdjs._shakeObjectExtension.noiseManager.getGenerator(name).noise(x, y);" + ], + "parameterObjects": "", + "useStrict": true, + "eventsSheetExpanded": true + } + ], + "expressionType": { + "type": "expression" + }, + "parameters": [ + { + "description": "Generator name", + "name": "Name", + "type": "string" + }, + { + "description": "X coordinate", + "name": "X", + "type": "expression" + }, + { + "description": "Y coordinate", + "name": "Y", + "type": "expression" + } + ], + "objectGroups": [] + }, + { + "description": "Generate a number from 3 dimensional simplex noise.", + "fullName": "3D noise", + "functionType": "Expression", + "name": "Noise3d", + "private": true, + "sentence": "", + "events": [ + { + "type": "BuiltinCommonInstructions::JsCode", + "inlineCode": [ + "const name = eventsFunctionContext.getArgument(\"Name\");\r", + "const x = eventsFunctionContext.getArgument(\"X\");\r", + "const y = eventsFunctionContext.getArgument(\"Y\");\r", + "const z = eventsFunctionContext.getArgument(\"Z\");\r", + "\r", + "eventsFunctionContext.returnValue = gdjs._shakeObjectExtension.noiseManager.getGenerator(name).noise(x, y, z);" + ], + "parameterObjects": "", + "useStrict": true, + "eventsSheetExpanded": true + } + ], + "expressionType": { + "type": "expression" + }, + "parameters": [ + { + "description": "Generator name", + "name": "Name", + "type": "string" + }, + { + "description": "X coordinate", + "name": "X", + "type": "expression" + }, + { + "description": "Y coordinate", + "name": "Y", + "type": "expression" + }, + { + "description": "Z coordinate", + "name": "Z", + "type": "expression" + } + ], + "objectGroups": [] + }, + { + "description": "Generate a number from 4 dimensional simplex noise.", + "fullName": "4D noise", + "functionType": "Expression", + "name": "Noise4d", + "private": true, + "sentence": "", + "events": [ + { + "type": "BuiltinCommonInstructions::JsCode", + "inlineCode": [ + "const name = eventsFunctionContext.getArgument(\"Name\");\r", + "const x = eventsFunctionContext.getArgument(\"X\");\r", + "const y = eventsFunctionContext.getArgument(\"Y\");\r", + "const z = eventsFunctionContext.getArgument(\"Z\");\r", + "const w = eventsFunctionContext.getArgument(\"W\");\r", + "\r", + "eventsFunctionContext.returnValue = gdjs._shakeObjectExtension.noiseManager.getGenerator(name).noise(x, y, z, w);" + ], + "parameterObjects": "", + "useStrict": true, + "eventsSheetExpanded": true + } + ], + "expressionType": { + "type": "expression" + }, + "parameters": [ + { + "description": "Generator name", + "name": "Name", + "type": "string" + }, + { + "description": "X coordinate", + "name": "X", + "type": "expression" + }, + { + "description": "Y coordinate", + "name": "Y", + "type": "expression" + }, + { + "description": "Z coordinate", + "name": "Z", + "type": "expression" + }, + { + "description": "W coordinate", + "name": "W", + "type": "expression" + } + ], + "objectGroups": [] + }, + { + "description": "Create a noise generator with default settings (frequency = 1, octaves = 1, persistence = 0.5, lacunarity = 2).", + "fullName": "Create a noise generator", + "functionType": "Action", + "name": "Create", + "private": true, + "sentence": "Create a noise generator named _PARAM1_", + "events": [ + { + "type": "BuiltinCommonInstructions::JsCode", + "inlineCode": [ + "const name = eventsFunctionContext.getArgument(\"Name\");\r", + "\r", + "gdjs._shakeObjectExtension.noiseManager.getGenerator(name);" + ], + "parameterObjects": "", + "useStrict": true, + "eventsSheetExpanded": false + } + ], + "parameters": [ + { + "description": "Generator name", + "name": "Name", + "type": "string" + } + ], + "objectGroups": [] + }, + { + "description": "Delete a noise generator and loose its settings.", + "fullName": "Delete a noise generator", + "functionType": "Action", + "name": "Delete", + "private": true, + "sentence": "Delete _PARAM1_ noise generator", + "events": [ + { + "type": "BuiltinCommonInstructions::JsCode", + "inlineCode": [ + "const name = eventsFunctionContext.getArgument(\"Name\");\r", + "\r", + "gdjs._shakeObjectExtension.noiseManager.deleteGenerator(name);" + ], + "parameterObjects": "", + "useStrict": true, + "eventsSheetExpanded": false + } + ], + "parameters": [ + { + "description": "Generator name", + "name": "Name", + "type": "string" + } + ], + "objectGroups": [] + }, + { + "description": "Delete all noise generators and loose their settings.", + "fullName": "Delete all noise generators", + "functionType": "Action", + "name": "DeleteAll", + "private": true, + "sentence": "Delete all noise generators", + "events": [ + { + "type": "BuiltinCommonInstructions::JsCode", + "inlineCode": "gdjs._shakeObjectExtension.noiseManager.deleteAllGenerators();", + "parameterObjects": "", + "useStrict": true, + "eventsSheetExpanded": false + } + ], + "parameters": [], + "objectGroups": [] + }, + { + "description": "The seed is a number used to generate the random noise. Setting the same seed will result in the same random noise generation. It's for example useful to generate the same world, by saving this seed value and reusing it later to generate again a world.", + "fullName": "Noise seed", + "functionType": "Action", + "name": "SetSeed", + "private": true, + "sentence": "Change the noise seed to _PARAM1_", + "events": [ + { + "type": "BuiltinCommonInstructions::JsCode", + "inlineCode": "gdjs._shakeObjectExtension.noiseManager.setSeed(eventsFunctionContext.getArgument(\"Seed\"));", + "parameterObjects": "", + "useStrict": true, + "eventsSheetExpanded": false + } + ], + "parameters": [ + { + "description": "Seed", + "longDescription": "15 digits numbers maximum", + "name": "Seed", + "type": "expression" + } + ], + "objectGroups": [] + }, + { + "description": "Change the looping period on X used for noise generation. The noise will wrap-around on X.", + "fullName": "Noise looping period on X", + "functionType": "Action", + "name": "SetLoopPeriodX", + "private": true, + "sentence": "Change the looping period on X of _PARAM2_: _PARAM1_", + "events": [ + { + "type": "BuiltinCommonInstructions::JsCode", + "inlineCode": [ + "const name = eventsFunctionContext.getArgument(\"Name\");\r", + "\r", + "gdjs._shakeObjectExtension.noiseManager.getGenerator(name).xLoopPeriod = eventsFunctionContext.getArgument(\"LoopPeriod\");" + ], + "parameterObjects": "", + "useStrict": true, + "eventsSheetExpanded": false + } + ], + "parameters": [ + { + "description": "Looping period on X", + "name": "LoopPeriod", + "type": "expression" + }, + { + "description": "Generator name", + "name": "Name", + "type": "string" + } + ], + "objectGroups": [] + }, + { + "description": "Change the looping period on Y used for noise generation. The noise will wrap-around on Y.", + "fullName": "Noise looping period on Y", + "functionType": "Action", + "name": "SetLoopPeriodY", + "private": true, + "sentence": "Change the looping period on Y of _PARAM2_: _PARAM1_", + "events": [ + { + "type": "BuiltinCommonInstructions::JsCode", + "inlineCode": [ + "const name = eventsFunctionContext.getArgument(\"Name\");\r", + "\r", + "gdjs._shakeObjectExtension.noiseManager.getGenerator(name).yLoopPeriod = eventsFunctionContext.getArgument(\"LoopPeriod\");" + ], + "parameterObjects": "", + "useStrict": true, + "eventsSheetExpanded": true + } + ], + "parameters": [ + { + "description": "Looping period on Y", + "name": "LoopPeriod", + "type": "expression" + }, + { + "description": "Generator name", + "name": "Name", + "type": "string" + } + ], + "objectGroups": [] + }, + { + "description": "Change the base frequency used for noise generation. A lower frequency will zoom in the noise.", + "fullName": "Noise base frequency", + "functionType": "Action", + "name": "SetFrequency", + "private": true, + "sentence": "Change the noise frequency of _PARAM2_: _PARAM1_", + "events": [ + { + "type": "BuiltinCommonInstructions::JsCode", + "inlineCode": [ + "const name = eventsFunctionContext.getArgument(\"Name\");\r", + "\r", + "gdjs._shakeObjectExtension.noiseManager.getGenerator(name).frequency = eventsFunctionContext.getArgument(\"Frequency\");" + ], + "parameterObjects": "", + "useStrict": true, + "eventsSheetExpanded": false + } + ], + "parameters": [ + { + "description": "Frequency", + "name": "Frequency", + "type": "expression" + }, + { + "description": "Generator name", + "name": "Name", + "type": "string" + } + ], + "objectGroups": [] + }, + { + "description": "Change the number of octaves used for noise generation. It can be seen as layers of noise with different zoom.", + "fullName": "Noise octaves", + "functionType": "Action", + "name": "SetOctaves", + "private": true, + "sentence": "Change the number of noise octaves of _PARAM2_: _PARAM1_", + "events": [ + { + "type": "BuiltinCommonInstructions::JsCode", + "inlineCode": [ + "const name = eventsFunctionContext.getArgument(\"Name\");\r", + "\r", + "gdjs._shakeObjectExtension.noiseManager.getGenerator(name).octaves = eventsFunctionContext.getArgument(\"Octaves\");" + ], + "parameterObjects": "", + "useStrict": true, + "eventsSheetExpanded": false + } + ], + "parameters": [ + { + "description": "Octaves", + "name": "Octaves", + "type": "expression" + }, + { + "description": "Generator name", + "name": "Name", + "type": "string" + } + ], + "objectGroups": [] + }, + { + "description": "Change the persistence used for noise generation. At its default value \"0.5\", it halves the noise amplitude at each octave.", + "fullName": "Noise persistence", + "functionType": "Action", + "name": "SetPersistence", + "private": true, + "sentence": "Change the noise persistence of _PARAM2_: _PARAM1_", + "events": [ + { + "type": "BuiltinCommonInstructions::JsCode", + "inlineCode": [ + "const name = eventsFunctionContext.getArgument(\"Name\");\r", + "\r", + "gdjs._shakeObjectExtension.noiseManager.getGenerator(name).persistence = eventsFunctionContext.getArgument(\"Persistence\");" + ], + "parameterObjects": "", + "useStrict": true, + "eventsSheetExpanded": false + } + ], + "parameters": [ + { + "description": "Persistence", + "name": "Persistence", + "type": "expression" + }, + { + "description": "Generator name", + "name": "Name", + "type": "string" + } + ], + "objectGroups": [] + }, + { + "description": "Change the lacunarity used for noise generation. At its default value \"2\", it doubles the frequency at each octave.", + "fullName": "Noise lacunarity", + "functionType": "Action", + "name": "SetLacunarity", + "private": true, + "sentence": "Change the noise lacunarity of _PARAM2_: _PARAM1_", + "events": [ + { + "type": "BuiltinCommonInstructions::JsCode", + "inlineCode": [ + "const name = eventsFunctionContext.getArgument(\"Name\");\r", + "\r", + "gdjs._shakeObjectExtension.noiseManager.getGenerator(name).lacunarity = eventsFunctionContext.getArgument(\"Lacunarity\");" + ], + "parameterObjects": "", + "useStrict": true, + "eventsSheetExpanded": false + } + ], + "parameters": [ + { + "description": "Lacunarity", + "name": "Lacunarity", + "type": "expression" + }, + { + "description": "Generator name", + "name": "Name", + "type": "string" + } + ], + "objectGroups": [] + }, + { + "description": "The seed used for noise generation.", + "fullName": "Noise seed", + "functionType": "Expression", + "name": "Seed", + "private": true, + "sentence": "", + "events": [ + { + "type": "BuiltinCommonInstructions::JsCode", + "inlineCode": "eventsFunctionContext.returnValue = gdjs._shakeObjectExtension.noiseManager.seed;", + "parameterObjects": "", + "useStrict": true, + "eventsSheetExpanded": false + } + ], + "expressionType": { + "type": "expression" + }, + "parameters": [], + "objectGroups": [] + }, + { + "description": "The base frequency used for noise generation.", + "fullName": "Noise base frequency", + "functionType": "Expression", + "name": "Frequency", + "private": true, + "sentence": "", + "events": [ + { + "type": "BuiltinCommonInstructions::JsCode", + "inlineCode": [ + "const name = eventsFunctionContext.getArgument(\"Name\");\r", + "\r", + "eventsFunctionContext.returnValue = gdjs._shakeObjectExtension.noiseManager.getGenerator(name).frequency;" + ], + "parameterObjects": "", + "useStrict": true, + "eventsSheetExpanded": false + } + ], + "expressionType": { + "type": "expression" + }, + "parameters": [ + { + "description": "Generator name", + "name": "Name", + "type": "string" + } + ], + "objectGroups": [] + }, + { + "description": "The number of octaves used for noise generation.", + "fullName": "Noise octaves number", + "functionType": "Expression", + "name": "Octaves", + "private": true, + "sentence": "", + "events": [ + { + "type": "BuiltinCommonInstructions::JsCode", + "inlineCode": [ + "const name = eventsFunctionContext.getArgument(\"Name\");\r", + "\r", + "eventsFunctionContext.returnValue = gdjs._shakeObjectExtension.noiseManager.getGenerator(name).octaves;" + ], + "parameterObjects": "", + "useStrict": true, + "eventsSheetExpanded": false + } + ], + "expressionType": { + "type": "expression" + }, + "parameters": [ + { + "description": "Generator name", + "name": "Name", + "type": "string" + } + ], + "objectGroups": [] + }, + { + "description": "The persistence used for noise generation.", + "fullName": "Noise persistence", + "functionType": "Expression", + "name": "Persistence", + "private": true, + "sentence": "", + "events": [ + { + "type": "BuiltinCommonInstructions::JsCode", + "inlineCode": [ + "const name = eventsFunctionContext.getArgument(\"Name\");\r", + "\r", + "eventsFunctionContext.returnValue = gdjs._shakeObjectExtension.noiseManager.getGenerator(name).persistence;" + ], + "parameterObjects": "", + "useStrict": true, + "eventsSheetExpanded": false + } + ], + "expressionType": { + "type": "expression" + }, + "parameters": [ + { + "description": "Generator name", + "name": "Name", + "type": "string" + } + ], + "objectGroups": [] + }, + { + "description": "The lacunarity used for noise generation.", + "fullName": "Noise lacunarity", + "functionType": "Expression", + "name": "Lacunarity", + "private": true, + "sentence": "", + "events": [ + { + "type": "BuiltinCommonInstructions::JsCode", + "inlineCode": [ + "const name = eventsFunctionContext.getArgument(\"Name\");\r", + "\r", + "eventsFunctionContext.returnValue = gdjs._shakeObjectExtension.noiseManager.getGenerator(name).lacunarity;" + ], + "parameterObjects": "", + "useStrict": true, + "eventsSheetExpanded": false + } + ], + "expressionType": { + "type": "expression" + }, + "parameters": [ + { + "description": "Generator name", + "name": "Name", + "type": "string" + } + ], + "objectGroups": [] + } + ], + "eventsFunctionsFolderStructure": { + "folderName": "__ROOT", + "children": [ + { + "functionName": "onFirstSceneLoaded" + }, + { + "functionName": "DefineHelperClasses" + }, + { + "functionName": "Noise2d" + }, + { + "functionName": "Noise3d" + }, + { + "functionName": "Noise4d" + }, + { + "functionName": "Create" + }, + { + "functionName": "Delete" + }, + { + "functionName": "DeleteAll" + }, + { + "functionName": "SetSeed" + }, + { + "functionName": "SetLoopPeriodX" + }, + { + "functionName": "SetLoopPeriodY" + }, + { + "functionName": "SetFrequency" + }, + { + "functionName": "SetOctaves" + }, + { + "functionName": "SetPersistence" + }, + { + "functionName": "SetLacunarity" + }, + { + "functionName": "Seed" + }, + { + "functionName": "Frequency" + }, + { + "functionName": "Octaves" + }, + { + "functionName": "Persistence" + }, + { + "functionName": "Lacunarity" + } + ] + }, + "eventsBasedBehaviors": [ + { + "description": "Shake 3D objects with translation and rotation.", + "fullName": "3D shake", + "helpPath": "", + "iconUrl": "", + "name": "ShakeModel3D", + "objectType": "", + "previewIconUrl": "", + "quickCustomizationVisibility": "hidden", + "eventsFunctions": [ + { + "fullName": "", + "functionType": "Action", + "name": "onCreated", + "sentence": "", + "events": [ + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [ + { + "type": { + "value": "BooleanVariable" + }, + "parameters": [ + "IsStartingAtCreation", + "True", + "" + ] + } + ], + "actions": [ + { + "type": { + "value": "ShakeObject3D::DefineHelperClasses" + }, + "parameters": [ + "", + "" + ] + }, + { + "type": { + "value": "ShakeObject3D::ShakeModel3D::StartShaking" + }, + "parameters": [ + "Object", + "Behavior", + "0", + "" + ] + } + ] + } + ], + "parameters": [ + { + "description": "Object", + "name": "Object", + "type": "object" + }, + { + "description": "Behavior", + "name": "Behavior", + "supplementaryInformation": "ShakeObject3D::ShakeModel3D", + "type": "behavior" + } + ], + "objectGroups": [] + }, + { + "fullName": "", + "functionType": "Action", + "name": "doStepPostEvents", + "sentence": "", + "events": [ + { + "type": "BuiltinCommonInstructions::Comment", + "color": { + "b": 109, + "g": 230, + "r": 255, + "textB": 0, + "textG": 0, + "textR": 0 + }, + "comment": "Step time counters." + }, + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [], + "actions": [ + { + "type": { + "value": "SetNumberVariable" + }, + "parameters": [ + "Time", + "+", + "TimeDelta()" + ] + } + ] + }, + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [ + { + "type": { + "value": "ShakeObject3D::ShakeModel3D::IsShaking" + }, + "parameters": [ + "Object", + "Behavior", + "" + ] + } + ], + "actions": [ + { + "type": { + "value": "SetNumberVariable" + }, + "parameters": [ + "NoiseTime", + "+", + "TimeDelta() * Frequency" + ] + }, + { + "type": { + "value": "ShakeObject3D::ShakeModel3D::SetSharedPropertyEasingFactor" + }, + "parameters": [ + "Object", + "Behavior", + "=", + "min(Object.Behavior::StartEasingFactor(), Object.Behavior::StopEasingFactor())" + ] + } + ], + "events": [ + { + "type": "BuiltinCommonInstructions::Comment", + "color": { + "b": 109, + "g": 230, + "r": 255, + "textB": 0, + "textG": 0, + "textR": 0 + }, + "comment": "Shake the object.\nSave the object displacement to revert it in onScenePostEvents." + }, + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [ + { + "type": { + "value": "NumberVariable" + }, + "parameters": [ + "TranslationAmplitudeX", + "!=", + "0" + ] + } + ], + "actions": [ + { + "type": { + "value": "SetNumberVariable" + }, + "parameters": [ + "DeltaX", + "=", + "ShakeObject3D::Noise2d(\"\", NoiseTime, 1000) * TranslationAmplitudeX * EasingFactor" + ] + }, + { + "type": { + "value": "SetX" + }, + "parameters": [ + "Object", + "+", + "DeltaX" + ] + } + ] + }, + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [ + { + "type": { + "value": "NumberVariable" + }, + "parameters": [ + "TranslationAmplitudeY", + "!=", + "0" + ] + } + ], + "actions": [ + { + "type": { + "value": "SetNumberVariable" + }, + "parameters": [ + "DeltaY", + "=", + "ShakeObject3D::Noise2d(\"\", NoiseTime, 2000) * TranslationAmplitudeY * EasingFactor" + ] + }, + { + "type": { + "value": "SetY" + }, + "parameters": [ + "Object", + "+", + "DeltaY" + ] + } + ] + }, + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [ + { + "type": { + "value": "NumberVariable" + }, + "parameters": [ + "TranslationAmplitudeZ", + "!=", + "0" + ] + } + ], + "actions": [ + { + "type": { + "value": "SetNumberVariable" + }, + "parameters": [ + "DeltaZ", + "=", + "ShakeObject3D::Noise2d(\"\", NoiseTime, 3000) * TranslationAmplitudeZ * EasingFactor" + ] + }, + { + "type": { + "value": "Scene3D::Base3DBehavior::SetZ" + }, + "parameters": [ + "Object", + "Object3D", + "+", + "DeltaZ" + ] + } + ] + }, + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [ + { + "type": { + "value": "NumberVariable" + }, + "parameters": [ + "RotationAmplitudeX", + "!=", + "0" + ] + } + ], + "actions": [ + { + "type": { + "value": "SetNumberVariable" + }, + "parameters": [ + "DeltaAngleX", + "=", + "ShakeObject3D::Noise2d(\"\", NoiseTime, 4000) * RotationAmplitudeX * EasingFactor" + ] + }, + { + "type": { + "value": "Scene3D::Base3DBehavior::SetRotationX" + }, + "parameters": [ + "Object", + "Object3D", + "+", + "DeltaAngleX" + ] + } + ] + }, + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [ + { + "type": { + "value": "NumberVariable" + }, + "parameters": [ + "RotationAmplitudeY", + "!=", + "0" + ] + } + ], + "actions": [ + { + "type": { + "value": "SetNumberVariable" + }, + "parameters": [ + "DeltaAngleY", + "=", + "ShakeObject3D::Noise2d(\"\", NoiseTime, 5000) * RotationAmplitudeY * EasingFactor" + ] + }, + { + "type": { + "value": "Scene3D::Base3DBehavior::SetRotationY" + }, + "parameters": [ + "Object", + "Object3D", + "+", + "DeltaAngleY" + ] + } + ] + }, + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [ + { + "type": { + "value": "NumberVariable" + }, + "parameters": [ + "RotationAmplitudeZ", + "!=", + "0" + ] + } + ], + "actions": [ + { + "type": { + "value": "SetNumberVariable" + }, + "parameters": [ + "DeltaAngleZ", + "=", + "ShakeObject3D::Noise2d(\"\", NoiseTime, 6000) * RotationAmplitudeZ * EasingFactor" + ] + }, + { + "type": { + "value": "SetAngle" + }, + "parameters": [ + "Object", + "+", + "DeltaAngleZ" + ] + } + ] + } + ] + } + ], + "parameters": [ + { + "description": "Object", + "name": "Object", + "type": "object" + }, + { + "description": "Behavior", + "name": "Behavior", + "supplementaryInformation": "ShakeObject3D::ShakeModel3D", + "type": "behavior" + } + ], + "objectGroups": [] + }, + { + "fullName": "", + "functionType": "Action", + "name": "doStepPreEvents", + "sentence": "", + "events": [ + { + "type": "BuiltinCommonInstructions::Comment", + "color": { + "b": 109, + "g": 230, + "r": 255, + "textB": 0, + "textG": 0, + "textR": 0 + }, + "comment": "Revert the shaking." + }, + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [ + { + "type": { + "value": "ShakeObject3D::ShakeModel3D::IsShaking" + }, + "parameters": [ + "Object", + "Behavior", + "" + ] + } + ], + "actions": [], + "events": [ + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [ + { + "type": { + "value": "NumberVariable" + }, + "parameters": [ + "TranslationAmplitudeX", + "!=", + "0" + ] + } + ], + "actions": [ + { + "type": { + "value": "SetX" + }, + "parameters": [ + "Object", + "-", + "DeltaX" + ] + } + ] + }, + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [ + { + "type": { + "value": "NumberVariable" + }, + "parameters": [ + "TranslationAmplitudeY", + "!=", + "0" + ] + } + ], + "actions": [ + { + "type": { + "value": "SetY" + }, + "parameters": [ + "Object", + "-", + "DeltaY" + ] + } + ] + }, + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [ + { + "type": { + "value": "NumberVariable" + }, + "parameters": [ + "TranslationAmplitudeZ", + "!=", + "0" + ] + } + ], + "actions": [ + { + "type": { + "value": "Scene3D::Base3DBehavior::SetZ" + }, + "parameters": [ + "Object", + "Object3D", + "-", + "DeltaZ" + ] + } + ] + }, + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [ + { + "type": { + "value": "NumberVariable" + }, + "parameters": [ + "RotationAmplitudeX", + "!=", + "0" + ] + } + ], + "actions": [ + { + "type": { + "value": "Scene3D::Base3DBehavior::SetRotationX" + }, + "parameters": [ + "Object", + "Object3D", + "-", + "DeltaAngleX" + ] + } + ] + }, + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [ + { + "type": { + "value": "NumberVariable" + }, + "parameters": [ + "RotationAmplitudeY", + "!=", + "0" + ] + } + ], + "actions": [ + { + "type": { + "value": "Scene3D::Base3DBehavior::SetRotationY" + }, + "parameters": [ + "Object", + "Object3D", + "-", + "DeltaAngleY" + ] + } + ] + }, + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [ + { + "type": { + "value": "NumberVariable" + }, + "parameters": [ + "RotationAmplitudeZ", + "!=", + "0" + ] + } + ], + "actions": [ + { + "type": { + "value": "SetAngle" + }, + "parameters": [ + "Object", + "-", + "DeltaAngleZ" + ] + } + ] + } + ] + } + ], + "parameters": [ + { + "description": "Object", + "name": "Object", + "type": "object" + }, + { + "description": "Behavior", + "name": "Behavior", + "supplementaryInformation": "ShakeObject3D::ShakeModel3D", + "type": "behavior" + } + ], + "objectGroups": [] + }, + { + "description": "Shake the object with a linear easing at the start and the end.", + "fullName": "Shake", + "functionType": "Action", + "name": "Shake", + "sentence": "Shake _PARAM0_ for _PARAM2_ seconds with _PARAM3_ seconds of easing to start and _PARAM4_ seconds to stop", + "events": [ + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [], + "actions": [ + { + "type": { + "value": "SetNumberVariable" + }, + "parameters": [ + "Time", + "=", + "0" + ] + }, + { + "type": { + "value": "SetNumberVariable" + }, + "parameters": [ + "NoiseTime", + "=", + "TimeFromStart() * Frequency" + ] + }, + { + "type": { + "value": "SetNumberVariable" + }, + "parameters": [ + "Duration", + "=", + "NewDuration" + ] + }, + { + "type": { + "value": "SetNumberVariable" + }, + "parameters": [ + "StartEasingDuration", + "=", + "StartEaseDuration" + ] + }, + { + "type": { + "value": "SetNumberVariable" + }, + "parameters": [ + "StopEasingDuration", + "=", + "StopEaseDuration" + ] + } + ] + }, + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [ + { + "type": { + "value": "NumberVariable" + }, + "parameters": [ + "NewDuration", + "<", + "StartEaseDuration + StopEaseDuration" + ] + } + ], + "actions": [ + { + "type": { + "value": "SetNumberVariable" + }, + "parameters": [ + "StartEasingDuration", + "=", + "StartEaseDuration * NewDuration / (StartEaseDuration + StopEaseDuration)" + ] + }, + { + "type": { + "value": "SetNumberVariable" + }, + "parameters": [ + "StopEasingDuration", + "=", + "StopEaseDuration * NewDuration / (StartEaseDuration + StopEaseDuration)" + ] + } + ] + } + ], + "parameters": [ + { + "description": "Object", + "name": "Object", + "type": "object" + }, + { + "description": "Behavior", + "name": "Behavior", + "supplementaryInformation": "ShakeObject3D::ShakeModel3D", + "type": "behavior" + }, + { + "description": "Duration (in seconds)", + "name": "NewDuration", + "type": "expression" + }, + { + "description": "Ease duration to start (in seconds)", + "name": "StartEaseDuration", + "type": "expression" + }, + { + "description": "Ease duration to stop (in seconds)", + "name": "StopEaseDuration", + "type": "expression" + } + ], + "objectGroups": [] + }, + { + "description": "Shake the object with a linear easing at the start and keep shaking until the stop action is used.", + "fullName": "Start shaking", + "functionType": "Action", + "name": "StartShaking", + "sentence": "Start shaking _PARAM0_ with _PARAM2_ seconds of easing", + "events": [ + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [], + "actions": [ + { + "type": { + "value": "SetNumberVariable" + }, + "parameters": [ + "Time", + "=", + "0" + ] + }, + { + "type": { + "value": "SetNumberVariable" + }, + "parameters": [ + "NoiseTime", + "=", + "TimeFromStart() * Frequency" + ] + }, + { + "type": { + "value": "SetNumberVariable" + }, + "parameters": [ + "Duration", + "=", + "1234567890" + ] + }, + { + "type": { + "value": "SetNumberVariable" + }, + "parameters": [ + "StartEasingDuration", + "=", + "EaseDuration" + ] + } + ] + } + ], + "parameters": [ + { + "description": "Object", + "name": "Object", + "type": "object" + }, + { + "description": "Behavior", + "name": "Behavior", + "supplementaryInformation": "ShakeObject3D::ShakeModel3D", + "type": "behavior" + }, + { + "description": "Ease duration (in seconds)", + "name": "EaseDuration", + "type": "expression" + } + ], + "objectGroups": [] + }, + { + "description": "Stop shaking the object with a linear easing.", + "fullName": "Stop shaking", + "functionType": "Action", + "name": "StopShaking", + "sentence": "Stop shaking _PARAM0_ with _PARAM2_ seconds of easing", + "events": [ + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [ + { + "type": { + "value": "ShakeObject3D::ShakeModel3D::IsShaking" + }, + "parameters": [ + "Object", + "Behavior", + "" + ] + } + ], + "actions": [ + { + "type": { + "value": "SetNumberVariable" + }, + "parameters": [ + "Time", + "=", + "0" + ] + }, + { + "type": { + "value": "SetNumberVariable" + }, + "parameters": [ + "Duration", + "=", + "EaseDuration" + ] + }, + { + "type": { + "value": "SetNumberVariable" + }, + "parameters": [ + "StopEasingDuration", + "=", + "EaseDuration" + ] + } + ] + } + ], + "parameters": [ + { + "description": "Object", + "name": "Object", + "type": "object" + }, + { + "description": "Behavior", + "name": "Behavior", + "supplementaryInformation": "ShakeObject3D::ShakeModel3D", + "type": "behavior" + }, + { + "description": "Ease duration (in seconds)", + "name": "EaseDuration", + "type": "expression" + } + ], + "objectGroups": [] + }, + { + "description": "Check if the object is shaking.", + "fullName": "Is shaking", + "functionType": "Condition", + "name": "IsShaking", + "sentence": "_PARAM0_ is shaking", + "events": [ + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [ + { + "type": { + "value": "NumberVariable" + }, + "parameters": [ + "Time", + "<", + "Duration" + ] + }, + { + "type": { + "value": "NumberVariable" + }, + "parameters": [ + "Time", + ">", + "0" + ] + } + ], + "actions": [ + { + "type": { + "value": "SetReturnBoolean" + }, + "parameters": [ + "True" + ] + } + ] + } + ], + "parameters": [ + { + "description": "Object", + "name": "Object", + "type": "object" + }, + { + "description": "Behavior", + "name": "Behavior", + "supplementaryInformation": "ShakeObject3D::ShakeModel3D", + "type": "behavior" + } + ], + "objectGroups": [] + }, + { + "description": "Check if the object is stopping to shake.", + "fullName": "Is stopping to shake", + "functionType": "Condition", + "name": "IsStopping", + "sentence": "_PARAM0_ is stopping to shake", + "events": [ + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [ + { + "type": { + "value": "ShakeObject3D::ShakeModel3D::IsShaking" + }, + "parameters": [ + "Object", + "Behavior", + "" + ] + }, + { + "type": { + "value": "ShakeObject3D::ShakeModel3D::StopEasingFactor" + }, + "parameters": [ + "Object", + "Behavior", + "<", + "1", + "" + ] + } + ], + "actions": [ + { + "type": { + "value": "SetReturnBoolean" + }, + "parameters": [ + "True" + ] + } + ] + } + ], + "parameters": [ + { + "description": "Object", + "name": "Object", + "type": "object" + }, + { + "description": "Behavior", + "name": "Behavior", + "supplementaryInformation": "ShakeObject3D::ShakeModel3D", + "type": "behavior" + } + ], + "objectGroups": [] + }, + { + "description": "the shaking frequency of the object.", + "fullName": "Shaking frequency", + "functionType": "ExpressionAndCondition", + "group": "ShakeObject3D configuration", + "name": "Frequency", + "sentence": "the shaking frequency", + "events": [ + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [], + "actions": [ + { + "type": { + "value": "SetReturnNumber" + }, + "parameters": [ + "Frequency" + ] + } + ] + } + ], + "expressionType": { + "type": "expression" + }, + "parameters": [ + { + "description": "Object", + "name": "Object", + "type": "object" + }, + { + "description": "Behavior", + "name": "Behavior", + "supplementaryInformation": "ShakeObject3D::ShakeModel3D", + "type": "behavior" + } + ], + "objectGroups": [] + }, + { + "fullName": "", + "functionType": "ActionWithOperator", + "getterName": "Frequency", + "group": "ShakeObject3D configuration", + "name": "SetFrequency", + "sentence": "", + "events": [ + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [], + "actions": [ + { + "type": { + "value": "SetNumberVariable" + }, + "parameters": [ + "Frequency", + "=", + "Value" + ] + } + ] + } + ], + "parameters": [ + { + "description": "Object", + "name": "Object", + "type": "object" + }, + { + "description": "Behavior", + "name": "Behavior", + "supplementaryInformation": "ShakeObject3D::ShakeModel3D", + "type": "behavior" + } + ], + "objectGroups": [] + }, + { + "description": "Return the easing factor according to start properties.", + "fullName": "Start easing factor", + "functionType": "Expression", + "name": "StartEasingFactor", + "private": true, + "sentence": "", + "events": [ + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [ + { + "type": { + "value": "NumberVariable" + }, + "parameters": [ + "StartEasingDuration", + "<=", + "0" + ] + } + ], + "actions": [ + { + "type": { + "value": "SetReturnNumber" + }, + "parameters": [ + "1" + ] + } + ] + }, + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [ + { + "type": { + "value": "NumberVariable" + }, + "parameters": [ + "StartEasingDuration", + ">", + "0" + ] + } + ], + "actions": [ + { + "type": { + "value": "SetReturnNumber" + }, + "parameters": [ + "clamp(Time / StartEasingDuration, 0, 1)" + ] + } + ] + } + ], + "expressionType": { + "type": "expression" + }, + "parameters": [ + { + "description": "Object", + "name": "Object", + "type": "object" + }, + { + "description": "Behavior", + "name": "Behavior", + "supplementaryInformation": "ShakeObject3D::ShakeModel3D", + "type": "behavior" + } + ], + "objectGroups": [] + }, + { + "description": "Return the easing factor according to stop properties.", + "fullName": "Stop easing factor", + "functionType": "ExpressionAndCondition", + "name": "StopEasingFactor", + "private": true, + "sentence": "stop easing factor", + "events": [ + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [ + { + "type": { + "value": "NumberVariable" + }, + "parameters": [ + "StopEasingDuration", + "<=", + "0" + ] + } + ], + "actions": [], + "events": [ + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [ + { + "type": { + "value": "NumberVariable" + }, + "parameters": [ + "Time", + "<", + "Duration" + ] + } + ], + "actions": [ + { + "type": { + "value": "SetReturnNumber" + }, + "parameters": [ + "1" + ] + } + ] + }, + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [ + { + "type": { + "value": "NumberVariable" + }, + "parameters": [ + "Time", + ">=", + "Duration" + ] + } + ], + "actions": [ + { + "type": { + "value": "SetReturnNumber" + }, + "parameters": [ + "0" + ] + } + ] + } + ] + }, + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [ + { + "type": { + "value": "NumberVariable" + }, + "parameters": [ + "StopEasingDuration", + ">", + "0" + ] + } + ], + "actions": [ + { + "type": { + "value": "SetReturnNumber" + }, + "parameters": [ + "clamp((Duration - Time) / StopEasingDuration, 0, 1)" + ] + } + ] + } + ], + "expressionType": { + "type": "expression" + }, + "parameters": [ + { + "description": "Object", + "name": "Object", + "type": "object" + }, + { + "description": "Behavior", + "name": "Behavior", + "supplementaryInformation": "ShakeObject3D::ShakeModel3D", + "type": "behavior" + } + ], + "objectGroups": [] + } + ], + "eventsFunctionsFolderStructure": { + "folderName": "__ROOT", + "children": [ + { + "functionName": "onCreated" + }, + { + "functionName": "doStepPostEvents" + }, + { + "functionName": "doStepPreEvents" + }, + { + "functionName": "Shake" + }, + { + "functionName": "StartShaking" + }, + { + "functionName": "StopShaking" + }, + { + "functionName": "IsShaking" + }, + { + "functionName": "IsStopping" + }, + { + "folderName": "ShakeObject3D configuration", + "children": [ + { + "functionName": "Frequency" + }, + { + "functionName": "SetFrequency" + } + ] + }, + { + "functionName": "StartEasingFactor" + }, + { + "functionName": "StopEasingFactor" + } + ] + }, + "propertyDescriptors": [ + { + "value": "0", + "type": "Number", + "unit": "Pixel", + "label": "Translation amplitude on X axis", + "name": "TranslationAmplitudeX" + }, + { + "value": "0", + "type": "Number", + "unit": "Pixel", + "label": "Translation amplitude on Y axis", + "name": "TranslationAmplitudeY" + }, + { + "value": "0", + "type": "Number", + "unit": "Pixel", + "label": "Translation amplitude on Z axis", + "name": "TranslationAmplitudeZ" + }, + { + "value": "5", + "type": "Number", + "unit": "DegreeAngle", + "label": "Rotation amplitude around X axis", + "name": "RotationAmplitudeX" + }, + { + "value": "5", + "type": "Number", + "unit": "DegreeAngle", + "label": "Rotation amplitude around Y axis", + "name": "RotationAmplitudeY" + }, + { + "value": "5", + "type": "Number", + "unit": "DegreeAngle", + "label": "Rotation amplitude around Z axis", + "name": "RotationAmplitudeZ" + }, + { + "value": "", + "type": "Number", + "label": "", + "hidden": true, + "name": "Time" + }, + { + "value": "", + "type": "Number", + "label": "", + "hidden": true, + "name": "Duration" + }, + { + "value": "", + "type": "Number", + "label": "", + "hidden": true, + "name": "StartEasingDuration" + }, + { + "value": "", + "type": "Number", + "label": "", + "hidden": true, + "name": "StopEasingDuration" + }, + { + "value": "", + "type": "Number", + "label": "Frequency", + "name": "Frequency" + }, + { + "value": "", + "type": "Number", + "label": "", + "hidden": true, + "name": "DeltaX" + }, + { + "value": "", + "type": "Number", + "label": "", + "hidden": true, + "name": "DeltaY" + }, + { + "value": "", + "type": "Number", + "label": "", + "hidden": true, + "name": "DeltaZ" + }, + { + "value": "", + "type": "Number", + "label": "", + "hidden": true, + "name": "DeltaAngleX" + }, + { + "value": "", + "type": "Number", + "label": "", + "hidden": true, + "name": "DeltaAngleY" + }, + { + "value": "", + "type": "Number", + "label": "", + "hidden": true, + "name": "DeltaAngleZ" + }, + { + "value": "", + "type": "Number", + "label": "", + "hidden": true, + "name": "NoiseTime" + }, + { + "value": "", + "type": "Behavior", + "label": "3D capability", + "extraInformation": [ + "Scene3D::Base3DBehavior" + ], + "choices": [], + "name": "Object3D" + }, + { + "value": "", + "type": "Boolean", + "label": "Start to shake at the object creation", + "name": "IsStartingAtCreation" + } + ], + "propertiesFolderStructure": { + "folderName": "__ROOT", + "children": [ + { + "propertyName": "TranslationAmplitudeX" + }, + { + "propertyName": "TranslationAmplitudeY" + }, + { + "propertyName": "TranslationAmplitudeZ" + }, + { + "propertyName": "RotationAmplitudeX" + }, + { + "propertyName": "RotationAmplitudeY" + }, + { + "propertyName": "RotationAmplitudeZ" + }, + { + "propertyName": "Time" + }, + { + "propertyName": "Duration" + }, + { + "propertyName": "StartEasingDuration" + }, + { + "propertyName": "StopEasingDuration" + }, + { + "propertyName": "Frequency" + }, + { + "propertyName": "DeltaX" + }, + { + "propertyName": "DeltaY" + }, + { + "propertyName": "DeltaZ" + }, + { + "propertyName": "DeltaAngleX" + }, + { + "propertyName": "DeltaAngleY" + }, + { + "propertyName": "DeltaAngleZ" + }, + { + "propertyName": "NoiseTime" + }, + { + "propertyName": "Object3D" + }, + { + "propertyName": "IsStartingAtCreation" + } + ] + }, + "sharedPropertyDescriptors": [ + { + "value": "", + "type": "Number", + "label": "", + "hidden": true, + "name": "EasingFactor" + } + ], + "sharedPropertiesFolderStructure": { + "folderName": "__ROOT", + "children": [ + { + "propertyName": "EasingFactor" + } + ] + } + } + ], + "eventsBasedObjects": [] } ], "externalLayouts": [] diff --git a/examples/starting-3d-tank/assets/Flat dark joystick border.png b/examples/starting-3d-tank/assets/Flat dark joystick border.png index 606bf85b223d68dbdd1be4f5ac851f2018e13828..b4e83e112eb9375cefbb86a6d044a972f8bf4dcf 100644 GIT binary patch literal 3751 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 delta 928 zcmV;R17G~79jgbB8Gi!+006uZmmdHC02@$DR7Ff_aWyqHHa0djH8nOiHZ3hJHa0dm zI5|R^?&H|1(F!EEFDZlRnwi_ z{vfi zj=HzjIAJ?^*aIR1yr3o5f0`Hd%5^<6F6Up zP~Fjl;*KVq#|bI9VKHMl8{r`)iPZ%{-xG$jf4=?q~^F0)IA24NSmi2{R2agHyOAVdeoQ zxPbQ(79!wkIWZ#NI!%xsUi&yP0Snv1t6i5;0fp`1zdpu#*d3k2|9q@mKxK#E_j(Kt z!LJ|`GN7)T^niore1Z;j$NmwypId1iKp=wpMU(OX1$5-Pn-+2DQ3 z3U8HfoorJ0VFQ$enSS6!7WRpkSLewMoFS+@5$ws+?STP?b}+=r0-O&JIbb}yTqpDq z_kD-lXD%rF#snxfL8d2)U`M4yN4i*I^UWcQjl?nfK%C*3a$s;&f%6R|JUAMx!11?+{;EmT?0T5AdBGkQj7xl&{aQ(}c)V~^X*e%3^Jziw;PzI z-Q)Pxrd*^ diff --git a/examples/starting-3d-tank/assets/Flat dark joystick thumb.png b/examples/starting-3d-tank/assets/Flat dark joystick thumb.png index acd005cc067032996264e510b58c8dbc0c102b62..8acf179f8660be36ccea1198105da35c07154fd7 100644 GIT binary patch literal 2939 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 L`njxgN@xNARMw6a literal 192 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 delta 4495 zcmY+Ic{~%~|G?35%S|dr48@!ssF*Z4Gr2NX5{9`qWXYH#rOT0X4AGpCGvv$?W6Erf zzKF0`XpXX4{MPrM-|zRw^Lnn&^L$>P*Yn5w{d&@5N>rZ7>PPeQa&d9p{6Krh=>)

CVi05h7CM;*%6n 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 literal 0 HcmV?d00001 diff --git a/examples/starting-3d-tank/assets/Target round button.png b/examples/starting-3d-tank/assets/Target round button.png index ac62c6af238ce3759f2b653820a757b74f1b37a9..e615798805c976c2a97557422b6358f983d6e6e3 100644 GIT binary patch literal 4371 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 A6aWAK diff --git a/examples/starting-3d-tile-placement/assets/StartingButtonTree.png b/examples/starting-3d-tile-placement/assets/StartingButtonTree.png index 238524c39a8e1aefcb789716f7b6dad4b5bf8800..d0a8d5d981c7de917239e7cb768976782b5578fe 100644 GIT binary patch delta 1587 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 DFmi=p diff --git a/examples/starting-3d-tile-placement/assets/StartingButtonUnit.png b/examples/starting-3d-tile-placement/assets/StartingButtonUnit.png index 5eb6c57d96a22c0e1ad68cf659700d338cec6834..d000b84ce7764c2388b68e5a6623187a75d6b239 100644 GIT binary patch delta 2292 zcmVttt40Do)Tk~JWgW@~-?~&ujOS0{(@CggObbsxN{=iNIyTR1nnS?v28A($_ z9Q0c2>wOh|I2B+b?_o0EpYLP^Hz}^g=bd2G-7rAe4|s>h+c>(nfrlqrngG%QI25gQ zBraB6oW7F7>8p9zg7Oot?YJcueZoZE$4|eoii1RemN^E;VTD2_(ByFY?gKUa{K6-#KLJOn`kblPK^-QD*5wROY1jW`s{Y8omKiF^2 z6|55Qtuif83A!R2qvqr$=kdJ+19j*PY`ESueas-mZ zbS)<8dt|x^8?Z*e6q$Zx(umDJq4ik%iCG~rKI)6&!@e;)$&$4^fTuDRK(S^?zs|kW z5P#_m{PHqXE%IrxsUjh>S;+BmJ+^cClC}(Hh6AY$SR)|zYx+hrJLTs8Ub&ux%xNDVj&iuAxSvr z;9*>S_D^x2nHPYu!=H+UkH)4R*oV%4UVp<<-GL2QBVb|0HGZ7UY=^94NnSQ+gZGQqC z)@pd`nHN#~;sH1gMgN7@Al9q+A~QU)Y6%@(Q(asCxJX{G78R(J86{|&(wtS^Uu^^YPR+S5iZPo_>14G+_FHf%CH zBYsGNE(<6(6xPqTRYZEjFt@ zof1ovF}{7>!K?pFQHhRRh9-uIjEC8xomAMG3;g8X3ex)OUGshO4F~0Ax_{R;8enWZ z0404?l?pTy^_oCsMUwCWUPoyE*HdNa4qw^Tz+5ehM$lWGSs`G5p^d3%4X-xN_kIVs zbwl755n;G-lW%L2WUZUeb!Q3gIy}VJhysz=wk1FTs<^3IyLiqCS`vu;83BM?PS~2JBdXO1gj`Ayl~d*&ej^19J^qu;v1+ zx#(vHWDTynef@RXLMxw}zLo_O=t~e}a5}u5Sisq!xltVzOvK>0fPYd(>)l_h=(e=L z(D3^gfyW1W6{zlR8hH=cvw#hWAPLjv72f+xkXkKX55NeX9Ikp#guNJ`Eg;lh7{%*# zp%*yvUFw@mT!Y4G3))aCNSyZ&)?C1k{TE$Ll5mxZ#OTrV)WL7_`alhs1%xh; zsQT&Gt#zS=EEG1AP=BjRtk;bghLO8Dr|j4?UYfwZJ5t7UP%^Rs8d$D3FAYov||#f(!(mm@`ypKeR-aLqOv68}zmw$TuPy_EF_}kLm-xn0lMiN~F zozP|KakJcdphgG0_CCeN|CG~HdPsCk3N2a zYL6!)B6bWE#W^9LKl9RWpGx@fcX?RW>@$~gmko%luo O0000EX>4Tx04R}tkv&MmKp2MKrb<;R4t5ZA z$j~}j5EXHhDi*;)X)CnqU~=gnG-*guTpR`0f`dPcRRt?f%Ws^E4huXp zWMoqF#35p_*uioKvy!0_PZNg~Rik_%>$1Xmi?dp(vDQ8L3xj!WCCznOqljY(2_zvx zMh#_DU?EDoMt_Ql6rIOB{KJkvNiLaOWiWCqpb8a|;|KqP-@P>pQq)e_ z00006VoOIv00000008+zyMF)x010qNS#tmYE+YT{E+YYWr9XB6000McNliru=?MuH z7Z(vat=j+q02y>eSad^gZEa<4bO1wgWnpw>WFU8GbZ8()Nlj2!fese{00xLjL_t(| z+U=W7Y=2x=6@b5Y-<$V0Gqy96G>KANH#n3)QY02p6cv@i!V!fuWKmeq1xuu$s)Rk= zbwgA|bWs+pip0(kq$shF5Ji!Nkg~7GgV#q_>Sm1KYMY|LYm+{t2(w>k);KmH|XjM4Tj~ zS&EdBJkJ3z3o&jslgIoG z=nwitaYVDxz!c_q0_gU-L~+c1A71-fk7F~P2Y$}6BnPHK4orm{o9Xb~<8@xWJi)a) zDI1**yC$c!m4wjc{2&NM&j0LN>mTa;gMZ}Hd;0wJ+jltgRECt2PPeNqNLm17S;in3 z6uXTx9cJ2tht}cS89(^SJ&v|>k~AR*!|?&o9~2J1`dq}}egFNE+N#I==wuQ7APh#U zMx_CyX-b?FsekI}^=<3>-2NWlIZ`Z}Q5;ntK%B$?eD8RKTg#N_eBld&VHSjA0DmAZ z0DN&eP^R;+KQ)4=gm7li3H2El%EMS z04N#`Z~nm{Ow>f{;*t-5>pJ660)KYRW_19+xjwmVeam<2{QS=rLI@mt3;+Q5UXlK< z{m!KuTHC07gEy}>0q|T;%aK?MfHDi#WBA3jCToL-H?uYnT>6X8rDcH-LR)u8TSG*M z!s&hje_XNHKOvdfC7JZ(fBfD?Ao$xo!D>g)i^Xs!IA$FoDJ5Z499*V%6@MqV)lT7* zW996r=QNxgYnT|RgkEHDw{H-o#+IHyO+b<+NGV59D-WPNwY_pY;kiR0OaHKl(&AZ^ z8oayl(Y5SYaBMo{UN7eSqD>Hn)P2ADbAXhRAS@0-r=IT6@+Dt77;~^CF+au}W*Ts? zReY!AOTKaR{f5Ygb(JfX0e@xm&z*?bI}r^JM(~-(Ql4xVliR=gf*>vG9bnRvcowwl zl9n&2Z7TW4ni1zt=*t470P;K^)&AV!{s*4dOxW9$>}@_=B%eRh<@{osAPn$5wacN> z5{ToNJkNRYaK_`i)b#`QO+>tSINQ>2sU(1K*t2{7^MUeOEp>*9`+tH$W%?&c0>F_2 zedPd3ok@}?ZHp-bP&)t8(G;(iDF@)yGG02GZn0G@0BJF(<1;b3q|{y7suX~D7?mg5 z`T&%=Qjd@Op$8?V2Yemht#&ml>jL30%lqg z-BQ{}s{|m=i$Tq_Q-A#chBiu@)yROfh{o=QegLJ7(q?5QL4bB$KY-Fkr6$32xGsBk zrX4_OBUQ8)Py#>>qcP8 zetXl`4xqGA+N=nG;@0;3qRsW&4Q+_Ae6P;g z`3V4g&#zVq6oc|`?BaI;r{9|7;!;b=4x-Hq$+zR<_*P$N^R@Q9ZzGX9iBS02g-fVeo=^YkB#vv$M0ifnz(&`8TdCEWB<4+*)3~JUctP1ndJI oe}wx4DIf5A;FT*23$FwG8=!oqkye&yivR!s07*qoM6N<$g6h;P!2kdN diff --git a/examples/starting-3d-tile-placement/assets/StartingTiledGround.png b/examples/starting-3d-tile-placement/assets/StartingTiledGround.png index 7e2f4217b7034fb750d96e373daa7e017c8fa5e0..380baea6566db151e41f861a43578cc7cbc510e4 100644 GIT binary patch delta 301 zcmeyw`kHBivIPTMlDE4H!+#K5uy^@n1_lPs0*}aI1_nK45N51cYF`EvWH0gbb!C6W zE-EO;)ahR5JNXTxM*U?^7srr_TW@dqavd@dIr8yj_Jq2CT?G&SzE|^bJQ}oCY1Z>= z!YbW=o-+y8&E9^Z<#El|8#DNRds-G--Jdeady?0;*^w7+Y(F(O^M#0X@XLP>-mU9O zUS$Kc4j;HO$s%5?*LnHA%)6<&_u}$(zx#?g-TZgnJTr3pn@_JFubdLI^tbP%Gfv;w zm+bd{IqB~k<{RtfEKWMEjK^oBX>KKmaAw(Hp|Quduv0x9=&^>bP0l+XkKr$lXI delta 645 zcmV;00($-H0`dirBYy#fX+uL$Nkc;*aB^>EX>4Tx04R}tkv&MmP!xqvQ?*hm4t5Z6 z$WV2$AS&W0RV;#q(pG5I!Q`cX(4-+rad8w}3l9D)RvlcNb#-tR1i>E=cfm=~MM}Ic zDYS_3!sC6MchBLy`v9R)VXE0R0jQc~q~b9#n_CqFuL#125P!zdBQaA?q!%;rtgn0O zrMipqEbqQQt53b4)iZ;tk@NO-tvzPaI|?Ng+Nb9yjQM#E)E;U4G+SbXee- zVIz~8Ck_*f#SWG`n3W8bc$zq(s2b%9S(g>gTb$KOjkWH{Ul_`3D`~FN8b%yTNFWIj zGHNKJ0t->vHGfh}r06{6;U9ARNpi{LDua<@0ad7w96$IU{GP2@n3{BxLNTEG#kN02 zfWR)$tlReYv28a`0RJ;^rM3Ol1~B_cdcCbhj)4Ac;NrTi$$P-%4lwwnONQh~ep*7I z0KA{kH|2nVTOhdR&aHKh(+40!vs%6Z4i16QB4w|8yiB{db8i3kwC48%K-+SYlBK{u z00006VoOIv00000008+zyMF)x010qNS#tmYE+YT{E+YYWr9XB6000McNliru=?Vc1 zIssHCh^3KRC{D>qL_t(|+U?pQ4#gl8M9~2QqCr%2|68m10zq1e0L9-0PjF`gLk@<7 zaliYXAx53orPV1pYxFM5e)qlaJW`TXTCK9|cXQ6EjpMa>lQ97uHURbkXtkOiz`Eam z&!dvFNqNax*8N_RM(@jSqSdO=yR7@YB!w84e^DPPDa0u2e)a+E19%6MF##PQ0Bi!l fK7f6|-ur+*Ha~vqE~N<*00000NkvXXu0mjfuiqby diff --git a/examples/starting-3d-tile-placement/assets/starter_switch.wav b/examples/starting-3d-tile-placement/assets/starter_switch.wav new file mode 100644 index 0000000000000000000000000000000000000000..50df257800a3c238b6be7b0d150a2629bc86ed7e GIT binary patch literal 68288 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 literal 0 HcmV?d00001 diff --git a/examples/starting-3d-tile-placement/assets/starting_woodthump.wav b/examples/starting-3d-tile-placement/assets/starting_woodthump.wav new file mode 100644 index 0000000000000000000000000000000000000000..5949eb5a8c6f3abf7f87d9f10ed3a83be9ee84b9 GIT binary patch literal 35904 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≺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 N002ovPDHLkV1mWg)1?3a diff --git a/examples/starting-3d-top-down-rpg/assets/Border.png b/examples/starting-3d-top-down-rpg/assets/Border.png index bed7c2d72a206a4946cdcebd4e3972082bcaf0cc..fc209e345f8331ae85efa24563e4f9342c0bcccf 100644 GIT binary patch delta 1119 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 R??7*YNN#J1r(9?;{{>}*dx8J} diff --git a/examples/starting-3d-top-down-rpg/assets/Border2.png b/examples/starting-3d-top-down-rpg/assets/Border2.png index e9d7f4a4deabf45ec5b84bb81f868b43a0c0dd8d..5d5ab796adbfc84cbf336b7f52adc93b341467c0 100644 GIT binary patch literal 1031 zcmeAS@N?(olHy`uVBq!ia0vp^2_VeD1|%QND7OGoY)RhkE)4%caKYZ?lYt_f1s;*b z3=G^tAk28_ZrvZCAbW|YuPgg2b}>;)eddSL^BEYJr+B(JhE&{oJ9l^3l>m{p|Ftf9hl~Xq+Np05s z(DZ1|oS6&s#Q$G^rgHTE+s)@^W^PO1`E#MXAYy#kJ#d&w{orR>xMi`Aiy{ zO;-Mi|ITgKHjzExWn;yXRkbF+G`uHon%uBwgP_YbMmBrNTkWqlWxLOq_%Q#5s>J5A zAFOj*<$7+OGkOmBCW(Ldrz-5vw0`aLivN6>$( zJTzpiOjMTG{I+`Sn%7eK-{Kyhy(ee4r@sARLB*;fo+gIp`Y}hQzRZl7xv$IM%bs5D zER(Za^VvW1GtOq_&UhpIXTg>mcW@6}3+&7Svl z{)V?cH~N6|j(*0YYXl9A*Y&>%7ri`Z!rxUzV(U9*8Va~97TZ(xagJu$k(mn>jMmA= zg?^pJ3F0W3teb9sb^eS5_gh9L@1!rdtKFEmjfYqH*Y*Gg)&Pcu3|tB#4vZ~K8Vx|c z0M`NrCq@wmjRqzTs!1NGS5e?OZ@92;OX>@e0yCS_EY_E%Eo@A9vS6~tt}`swkIn=H zNNcPLJiq?;nZrtcb0oF27Te%%&mgl~;yf8T@_Q2t}=c>Oh>{iS&ng7Cn zq%QgCU+vV=o*!tmz%PC(g+@&1%B~C9mw&uq? z&LbB;+sm*26UKO-KW$>ELzPd_qD}9eOMl*OwBFXBtdt~C$*`1T|ElYDQngc-dfwdU zcuYY*hee@nqN|2dhs*MPuAEsb!URGxzGyaF3jMhM6YHVac=c;G7QcX5mci52&t;uc GLK6UZuEA>n literal 5983 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 literal 0 HcmV?d00001 diff --git a/examples/starting-3d-top-down-rpg/assets/Flat dark joystick border.png b/examples/starting-3d-top-down-rpg/assets/Flat dark joystick border.png index 606bf85b223d68dbdd1be4f5ac851f2018e13828..b4e83e112eb9375cefbb86a6d044a972f8bf4dcf 100644 GIT binary patch literal 3751 zcmV;Y4p{MtP)005u}1^@s6i_d2*00006VoOIv0RI60 z0RN!9r;`8x010qNS#tmYE+YT{E+YYWr9XB6000McNliru>IW486)D;&{k8xA02y>e zSad^gZEa<4bO1wgWnpw>WFU8GbZ8()Nlj2!fese{01gjHL_t(|+U=cxXq@+Xz(3D> zC+SX79NUp>$yVSJ$>iiJ))lIUFb5nqkA@bSF^@SUfk8d!SOYF+9(=fJWpSxPJu{Ww|g-LpUR+CJNoe0RD#-Fx?Z zz*x5LUcdLeAD-v?eDC{yUoebKL@WgNh(uz+rWQAX1fn@1BG3aG0qFpq0NQ~LAn5&^ z&m55PexCtuflh)EfITSm(lNuxSW*0eB9GRYA65q#Of&1Dq9i`D~*IS=R#gJ@af2 zj`KC(dEj6T3HK;6z=yyua;ooNPMlAhfPRrKIz9et%)c z1gyd4AsE(!z+VD~e3a=*$&6<}hm+&y&zOL1TFX~8+}1|tj&1;^r>?WOFkkB#@jqOZzpk}x=+so!i|#vw z1TNINd`->Gbad{#`{yS*chc0{TV(oQ32JDOOiy-oAe7JDH zah!rByuFkmm&@Tpl8C;TyjZ>UhgB1>w|{Uz;55*KQOisyyp$Rr9jluX+#4Mn5;$k_ z`GOvSbG^~(%okQ%z^D5M4~lS_hwmaYYL|8qPCwm0c+l4+`04(^gASa+RZjp(-z>5~F8*BcRuCoU^i9H_Rz~wGxd<*~fr=(R~rG+co*> zX-o*G`}XdO)N}^ivp3eRsvpyMHwZNfNmTwInTVHdrFGddU?ds~A~t>^m~ zn{H_|GKhY8qw5JbRDvVXSO}4mCSOAmLgiE>8VhZjfZj6t0lO9rOVo3q`X#uxe{e|P zf9x6m?aypfs3UgP&wha;CZDmv zj`(QFC0O8F7DHo$yuRMIBhK38ci%y5!GW=H`n>1(6`27=Ubom8W#VX2O-OJ5vmJtS z#kyakOt`3YuGWmKo&m-AhRJ7?ksvDzSqdw`qI}b0W|Wz)7gU11ZFE1icp2qJbGau| zpIn$&KLd&!vQQb7Kpf|5g_Phwwon-r;)Qif@SZ)fb`9#ZH6TW%x?yo;+oenKxm7cu z26V{eGb-5)Yfy*QF2TZ(#mJ~;8GdjK_`Jo(s4&m1ItKK{e>d^Tv)hdN7zf_XfEvv0 zH~EZei~|YifmKSd9I)6J)j);S1Z2O3#;6hls|omw#l@%+U%IaeiNs>T;F2!d7|^Iz zBjPSfIGv8W4nR{T+h_6_)oet9ExE|u60G9bvptOJA`;CX14b+&Ms@uok}se=NZP0> z3cK?JR1?ssHc#aVD7NjIQEfW%OYnwwKiIN$8#}so<2a6mwoY*zht95DJicvvIS%&( z+?#rNqV&O}u)T99ZEY)$2X1Z&ky%`@t5t_Iw}c3XTM36-2?m?^eC7tKTI%82^93yF z;nGeZKsHwS`?p;rsd<{%XC`9+JCuwSK zF6HgPd;yEffZ^6Qc6Rp=2-=q?jZPpKq-$3X;kKfSXnVeZ1rus*rE_PuH5*1=AK=ir zql-|uwfJFN(eF?TxUrUqR9?W$;sP@_uA`c>h#LUpayh1_uCutXP_lq?#k^@@exAvz z6J%Z2qTk?TU6-kA6D%&w7yH)x?g=T{q~+z?T)%dOrA)@ILW8ojl;QfdD=aVHF6Hf+ zd;w=lO1;@EHzuz#Klg=QjRs?3evTX0ua-IocsB#ynMau~^(d;!XVX)-?lLVcA#N{O zMyywEFJ;IqF0zza;MVNTjUH|$U%;7-9_`l6&+Q5|K(ldU@@lybcPp<1>x_lhs4|mz z0!rHAVN{tbcLh|Kuy`0%=IXr+sA?I|s4^?gIRK85uz(m<)lW&@6>vH1q$y!C+AvDQ z&1Rf?0;c0}7c2`JRU?s3$KAXx7=YhdT#PF5$(W|=QDMf`d`<|$Z!JDXh51eXAMO`0#|IW4qr#kB zQ$WeeSsMo$6(l3BtSz8dn}20-GAhJ}`QyKbOYjO-hK)+`iwFN=6#;WUw>TMPYV_>nTXE< zzqGg+W#UX~JU&x+23$#*CoFD8nHXN{AJ!Dm4apf;SvGAL8z7w=A3amFfZi(UVT+%! zLEc&KAJ^ZWP2~hIYcVuRWY%kFOB2v*Ml1^&C86+R&!G}CARsDl<87s77)6jl<@lps z^hg0OCE}Bo1&tE2-*9Thos9|TWx?Z?1&wlNzk&tyvfy_uu0}btU%>);9r4@N5gP?C zDJpLj`r<&LFZ_IFGPAw2>lSdxVr;C#-y{>G9~S!Jq8`)Ww#W(W26kg@;_f5n{Wc_E zIv#gbdClT%tU5|l-b^RrnTiPrkV=e>06(;F8xJt-xj{t*^cs?HW4o#vdBxFHIabOW zOA;`dh|em#X>m6I6#k;v(pz*F{2(`HrhdP@v#T4}ZnB=M#!e%{1V+@3mASzTxT@D96d% zoS8bmy|b$m_=;VchTvaQg4 z1#_zM)l?#0_Jt@;CC)Z=@%)&=%h>cxJ&;l5~~2K|5#ML)^+@9v`f+2v$>q1ykkZ04u#>mPg-c-Jo- za>tG?THDs|qvq$n;Iru|-*mbPucgLE|8uiWKsGx9y?ggXMdc(gQ0rGqMA*^2o0c`k zf=ihUHzu#5s(z3!p(=;Vv82jZCHO&ZPEXBj>FEBEBXr_}<^78b^R%@+hU45nho5y_ zrY5hV{%AgiU6sS17Px&zU1z}ivf;{n;g5hwEnmH+re=2T>bWbSYL3aP6D%*^u66My zRCzO%7=6EH#@4kIotraL=`9`I!?m6bbGayN!>PpR`I;Km z)M~VPqk{)T_yG{B)pHEDwxSA|#f4fHZ%mcfO1Wvgp#=npL}Q_#=n>!_fG&(;m<9gM zU6d2)bi8ICIwXFGq_@)gE#P%*QNyFi0N+LB?P^ad*R6ne*b|L)spxl1#MQ-97)IrI zss_5-x)g8~5&s<6YNeK8Re9S_;`v&@{L-wVM+Ck_jZfWFfF#N=$O%7`+PW0*ei;W3 zBD?|oQ5~0O7x*R02{$8WYPq>+I05g_+aK#d^&2956F5*OA8Q2o87lAjIomaafDe@D z2%?98KLMWeQD$_3UjZMGlb37exR7 delta 928 zcmV;R17G~79jgbB8Gi!+006uZmmdHC02@$DR7Ff_aWyqHHa0djH8nOiHZ3hJHa0dm zI5|R^?&H|1(F!EEFDZlRnwi_ z{vfi zj=HzjIAJ?^*aIR1yr3o5f0`Hd%5^<6F6Up zP~Fjl;*KVq#|bI9VKHMl8{r`)iPZ%{-xG$jf4=?q~^F0)IA24NSmi2{R2agHyOAVdeoQ zxPbQ(79!wkIWZ#NI!%xsUi&yP0Snv1t6i5;0fp`1zdpu#*d3k2|9q@mKxK#E_j(Kt z!LJ|`GN7)T^niore1Z;j$NmwypId1iKp=wpMU(OX1$5-Pn-+2DQ3 z3U8HfoorJ0VFQ$enSS6!7WRpkSLewMoFS+@5$ws+?STP?b}+=r0-O&JIbb}yTqpDq z_kD-lXD%rF#snxfL8d2)U`M4yN4i*I^UWcQjl?nfK%C*3a$s;&f%6R|JUAMx!11?+{;EmT?0T5AdBGkQj7xl&{aQ(}c)V~^X*e%3^Jziw;PzI z-Q)Pxrd*^ diff --git a/examples/starting-3d-top-down-rpg/assets/Flat dark joystick thumb.png b/examples/starting-3d-top-down-rpg/assets/Flat dark joystick thumb.png index acd005cc067032996264e510b58c8dbc0c102b62..8acf179f8660be36ccea1198105da35c07154fd7 100644 GIT binary patch literal 2939 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 delta 645 zcmV;00($-H0`dirBYy#fX+uL$Nkc;*aB^>EX>4Tx04R}tkv&MmP!xqvQ?*hm4t5Z6 z$WV2$AS&W0RV;#q(pG5I!Q`cX(4-+rad8w}3l9D)RvlcNb#-tR1i>E=cfm=~MM}Ic zDYS_3!sC6MchBLy`v9R)VXE0R0jQc~q~b9#n_CqFuL#125P!zdBQaA?q!%;rtgn0O zrMipqEbqQQt53b4)iZ;tk@NO-tvzPaI|?Ng+Nb9yjQM#E)E;U4G+SbXee- zVIz~8Ck_*f#SWG`n3W8bc$zq(s2b%9S(g>gTb$KOjkWH{Ul_`3D`~FN8b%yTNFWIj zGHNKJ0t->vHGfh}r06{6;U9ARNpi{LDua<@0ad7w96$IU{GP2@n3{BxLNTEG#kN02 zfWR)$tlReYv28a`0RJ;^rM3Ol1~B_cdcCbhj)4Ac;NrTi$$P-%4lwwnONQh~ep*7I z0KA{kH|2nVTOhdR&aHKh(+40!vs%6Z4i16QB4w|8yiB{db8i3kwC48%K-+SYlBK{u z00006VoOIv00000008+zyMF)x010qNS#tmYE+YT{E+YYWr9XB6000McNliru=?Vc1 zIssHCh^3KRC{D>qL_t(|+U?pQ4#gl8M9~2QqCr%2|68m10zq1e0L9-0PjF`gLk@<7 zaliYXAx53orPV1pYxFM5e)qlaJW`TXTCK9|cXQ6EjpMa>lQ97uHURbkXtkOiz`Eam z&!dvFNqNax*8N_RM(@jSqSdO=yR7@YB!w84e^DPPDa0u2e)a+E19%6MF##PQ0Bi!l fK7f6|-ur+*Ha~vqE~N<*00000NkvXXu0mjfuiqby diff --git a/examples/starting-3d-top-down-rpg/assets/StartingWall.png b/examples/starting-3d-top-down-rpg/assets/StartingWall.png index ea50f81d90f72e4b0e673284634e86c6b076347c..a963c15fb01bfb514613d3a8beb2763128eef1d2 100644 GIT binary patch literal 429 zcmeAS@N?(olHy`uVBq!ia0vp^4Is?H1|$#LC7uRSY)RhkE)4%caKYZ?lYt_f1s;*b z3=G`DAk4@xYmNj^kiEpy*OmPhyBLqP?5poKlNcBntvp>ELn>~)y{pLA;vnMUn0BaF zK-+>t?5wRI$GIa~LOZxrjiXRS`Bb;!0SM0v#nEKNbw3DuFKT*v91fH&bF6*2UngIGpYBc}= literal 378 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 literal 0 HcmV?d00001 diff --git a/examples/starting-3d-top-down-rpg/starting-3d-top-down-rpg.json b/examples/starting-3d-top-down-rpg/starting-3d-top-down-rpg.json index 1f6557f2c..2fcde2fda 100644 --- a/examples/starting-3d-top-down-rpg/starting-3d-top-down-rpg.json +++ b/examples/starting-3d-top-down-rpg/starting-3d-top-down-rpg.json @@ -1,7 +1,7 @@ { "firstLayout": "", "gdVersion": { - "build": 272, + "build": 274, "major": 5, "minor": 6, "revision": 0 @@ -224,6 +224,34 @@ "name": "assets\\A button.png", "smoothed": true, "userAdded": false + }, + { + "file": "assets/DirtParticle.png", + "kind": "image", + "metadata": "", + "name": "assets/DirtParticle.png", + "smoothed": true, + "userAdded": false + }, + { + "file": "assets/starting_bookopen.wav", + "kind": "audio", + "metadata": "", + "name": "assets/starting_bookopen.wav", + "preloadAsMusic": false, + "preloadAsSound": false, + "preloadInCache": false, + "userAdded": false + }, + { + "file": "assets/starter_pop.wav", + "kind": "audio", + "metadata": "", + "name": "assets/starter_pop.wav", + "preloadAsMusic": false, + "preloadAsSound": false, + "preloadInCache": false, + "userAdded": false } ] }, @@ -256,7 +284,7 @@ "gridColor": 10401023, "gridAlpha": 0.8, "snap": false, - "zoomFactor": 0.2817259932079644, + "zoomFactor": 0.5843784410741105, "windowMask": false, "selectedLayer": "", "gameEditorMode": "embedded-game" @@ -492,13 +520,13 @@ "angle": 0, "customSize": true, "depth": 0, - "height": 120, + "height": 104, "keepRatio": true, "layer": "MobileControls", "locked": true, "name": "ActionButton", "persistentUuid": "0133b683-d9f1-45f0-947e-5511772fc059", - "width": 120, + "width": 104, "x": 1104, "y": 576, "zOrder": 23, @@ -612,6 +640,7 @@ { "assetStoreId": "9d5de415b6e469c681701ea42d3b1c486fee31d6ef591a2d3e226f639ab486ff", "name": "TwoChoicesDialogBox", + "persistentUuid": "26ac36eb-00ba-493a-9b9e-f5a513e5e4f1", "type": "TwoChoicesDialogBoxes::TwoChoicesDialogBox", "variant": "DialogBox", "variables": [], @@ -807,12 +836,13 @@ { "assetStoreId": "", "name": "NPC", + "persistentUuid": "354d6e82-38d7-4c08-a801-cd9c6c3adb37", "type": "Scene3D::Model3DObject", "variables": [], "effects": [], "behaviors": [], "content": { - "centerLocation": "ModelOrigin", + "centerLocation": "ObjectCenter", "crossfadeDuration": 0.10000000149011612, "depth": 100, "height": 100, @@ -897,6 +927,7 @@ "adaptCollisionMaskAutomatically": true, "assetStoreId": "", "name": "ActionButton", + "persistentUuid": "458751d3-3821-4d05-9235-3c9ffb34e3b5", "type": "Sprite", "updateIfNotVisible": false, "variables": [], @@ -911,6 +942,25 @@ "TouchIsInside": false, "MouseIsInside": false, "Index": 2.0247e-320 + }, + { + "name": "ButtonScaleTween", + "type": "ButtonStates::ButtonScaleTween", + "Scale": "Scale", + "ButtonFSM": "ButtonFSM", + "Tween": "Tween", + "IdleScale": 1.3, + "FocusedScale": 1.25, + "FadeInDuration": 0.02, + "FadeOutDuration": 0.05, + "PressedScale": 1.2, + "FadeInEasing": "easeOutQuad", + "FadeOutEasing": "easeInQuad", + "PreviousState": "Idle" + }, + { + "name": "Tween", + "type": "Tween::TweenBehavior" } ], "animations": [ @@ -1045,6 +1095,87 @@ ] } } + }, + { + "assetStoreId": "", + "name": "Debris", + "persistentUuid": "7be91ac9-81bc-4626-9f2f-0d269ae7645e", + "type": "ParticleEmitter3D::ParticleEmitter3D", + "variant": "", + "variables": [], + "effects": [], + "behaviors": [], + "content": { + "EndColor": "255;255;255", + "StartColor": "109;109;109", + "SpayConeAngle": 360, + "EndScale": 1, + "Gravity": 600, + "StartSpeedMax": 500, + "StartSizeMin": 32, + "Duration": 0.01, + "Flow": 2000, + "LifespanMin": 0.075, + "LifespanMax": 0.25, + "StartSizeMax": 32, + "StartSpeedMin": 300 + }, + "childrenContent": { + "Particle": { + "adaptCollisionMaskAutomatically": true, + "updateIfNotVisible": false, + "animations": [ + { + "name": "Image", + "useMultipleDirections": false, + "directions": [ + { + "looping": false, + "timeBetweenFrames": 0.08, + "sprites": [ + { + "hasCustomCollisionMask": true, + "image": "assets/DirtParticle.png", + "points": [], + "originPoint": { + "name": "origine", + "x": 0, + "y": 0 + }, + "centerPoint": { + "automatic": true, + "name": "centre", + "x": 0, + "y": 0 + }, + "customCollisionMask": [ + [ + { + "x": 0, + "y": 0 + }, + { + "x": 32, + "y": 0 + }, + { + "x": 32, + "y": 32 + }, + { + "x": 0, + "y": 32 + } + ] + ] + } + ] + } + ] + } + ] + } + } } ], "objectsFolderStructure": { @@ -1056,6 +1187,9 @@ { "objectName": "NPC" }, + { + "objectName": "Debris" + }, { "objectName": "Building_Obstacle" }, @@ -1224,6 +1358,18 @@ "", "\"Dialog layer\"" ] + }, + { + "type": { + "value": "PlaySound" + }, + "parameters": [ + "", + "assets/starting_bookopen.wav", + "", + "80", + "1.1" + ] } ] } @@ -1269,6 +1415,41 @@ } ], "actions": [ + { + "type": { + "value": "Create" + }, + "parameters": [ + "", + "Debris", + "NPC.CenterX()", + "NPC.CenterY()", + "" + ] + }, + { + "type": { + "value": "Scene3D::Base3DBehavior::SetCenterZ" + }, + "parameters": [ + "Debris", + "Object3D", + "=", + "NPC.Object3D::CenterZ()" + ] + }, + { + "type": { + "value": "PlaySound" + }, + "parameters": [ + "", + "assets/starter_pop.wav", + "", + "80", + "RandomFloatInRange(0.7,0.9)" + ] + }, { "type": { "value": "Delete" @@ -1329,6 +1510,18 @@ "", "\"Dialog layer\"" ] + }, + { + "type": { + "value": "PlaySound" + }, + "parameters": [ + "", + "assets/starting_bookopen.wav", + "", + "80", + "0.8" + ] } ] } @@ -1447,6 +1640,10 @@ "name": "ButtonFSM", "type": "ButtonStates::ButtonFSM" }, + { + "name": "ButtonScaleTween", + "type": "ButtonStates::ButtonScaleTween" + }, { "name": "Effect", "type": "EffectCapability::EffectBehavior" @@ -1486,6 +1683,10 @@ { "name": "TopDownMultitouchMapper", "type": "SpriteMultitouchJoystick::TopDownMultitouchMapper" + }, + { + "name": "Tween", + "type": "Tween::TweenBehavior" } ] } @@ -18352,11 +18553,13 @@ { "assetStoreId": "90d94b6e3a23c34948e59eaaef4679740f8ab588696d59b21e0c0c2ef106b94d", "name": "YesButton", + "persistentUuid": "8d80f548-1022-4481-ba31-4fdf850768da", "type": "PanelSpriteButton::PanelSpriteButton", "variant": "OptionButton", "variables": [ { "name": "ID", + "persistentUuid": "111a6781-3c8b-47b7-b062-c4558df1435e", "type": "number", "value": 0 } @@ -18395,298 +18598,315 @@ "BottomPadding": 8, "TopPadding": 8, "MinimalWidth": 300 - } - }, - { - "assetStoreId": "", - "bottomMargin": 16, - "height": 48, - "leftMargin": 16, - "name": "TextBorder", - "rightMargin": 16, - "texture": "Border.png", - "tiled": true, - "topMargin": 16, - "type": "PanelSpriteObject::PanelSprite", - "width": 48, - "variables": [], - "effects": [], - "behaviors": [ - { - "name": "Anchor", - "type": "AnchorBehavior::AnchorBehavior", - "bottomEdgeAnchor": 2, - "leftEdgeAnchor": 1, - "relativeToOriginalWindowSize": true, - "rightEdgeAnchor": 2, - "topEdgeAnchor": 1, - "useLegacyBottomAndRightAnchors": false - } - ] - }, - { - "assetStoreId": "", - "name": "Dialogue", - "type": "BitmapText::BitmapTextObject", - "variables": [], - "effects": [], - "behaviors": [ - { - "name": "Anchor", - "type": "AnchorBehavior::AnchorBehavior", - "bottomEdgeAnchor": 2, - "leftEdgeAnchor": 1, - "relativeToOriginalWindowSize": true, - "rightEdgeAnchor": 2, - "topEdgeAnchor": 1, - "useLegacyBottomAndRightAnchors": false - } - ], - "content": { - "text": "Default message. Continue?", - "opacity": 255, - "scale": 2, - "fontSize": 20, - "tint": "255;255;255", - "bitmapFontResourceName": "8BitOperatorPlus8Regular_Edited.fnt", - "textureAtlasResourceName": "8BitOperatorPlus8Regular_Edited.png", - "align": "left", - "wordWrap": true, - "verticalTextAlignment": "top" - } - } - ], - "objectsFolderStructure": { - "folderName": "__ROOT", - "children": [ - { - "objectName": "Dialogue" - }, - { - "objectName": "TextBorder" - }, - { - "objectName": "YesButton" - }, - { - "objectName": "NoButton" - } - ] - }, - "objectsGroups": [ - { - "name": "Buttons", - "objects": [ - { - "name": "NoButton" - }, - { - "name": "YesButton" - } - ] - } - ], - "layers": [ - { - "ambientLightColorB": 200, - "ambientLightColorG": 200, - "ambientLightColorR": 200, - "camera2DPlaneMaxDrawingDistance": 5000, - "camera3DFarPlaneDistance": 10000, - "camera3DFieldOfView": 45, - "camera3DNearPlaneDistance": 3, - "cameraType": "", - "followBaseLayerCamera": false, - "isLightingLayer": false, - "isLocked": false, - "name": "", - "renderingType": "", - "visibility": true, - "cameras": [ - { - "defaultSize": true, - "defaultViewport": true, - "height": 0, - "viewportBottom": 1, - "viewportLeft": 0, - "viewportRight": 1, - "viewportTop": 0, - "width": 0 - } - ], - "effects": [] - } - ], - "instances": [ - { - "angle": 0, - "customSize": true, - "depth": 1, - "height": 124, - "layer": "", - "name": "Dialogue", - "persistentUuid": "c7183394-9ca6-4c16-9d13-f0b7cfe803d8", - "width": 554, - "x": 19, - "y": 18, - "zOrder": 45, - "numberProperties": [], - "stringProperties": [], - "initialVariables": [] - }, - { - "angle": 0, - "customSize": true, - "depth": 0, - "height": 213, - "layer": "", - "name": "TextBorder", - "persistentUuid": "639d5661-817d-4faa-b33b-c0ff129c309a", - "width": 600, - "x": 0, - "y": 0, - "zOrder": 43, - "numberProperties": [], - "stringProperties": [], - "initialVariables": [] - }, - { - "angle": 0, - "customSize": true, - "depth": 32, - "height": 36, - "layer": "", - "name": "YesButton", - "persistentUuid": "a0ca78d3-0a23-4bbe-89de-fb2d0a53996c", - "width": 105, - "x": 350, - "y": 160, - "zOrder": 510, - "numberProperties": [], - "stringProperties": [], - "initialVariables": [] - }, - { - "angle": 0, - "customSize": true, - "depth": 32, - "height": 36, - "layer": "", - "name": "NoButton", - "persistentUuid": "6781aafa-3ae9-4820-bb97-682a524a0cf6", - "width": 103, - "x": 471, - "y": 160, - "zOrder": 510, - "numberProperties": [], - "stringProperties": [], - "initialVariables": [ - { - "name": "ID", - "persistentUuid": "bb563dbe-93eb-4412-b843-f60016b9a893", - "type": "number", - "value": 1 - } - ] - } - ], - "editionSettings": {} - }, - { - "areaMaxX": 600, - "areaMaxY": 213, - "areaMaxZ": 32, - "areaMinX": 0, - "areaMinY": 0, - "areaMinZ": 0, - "assetStoreAssetId": "9d5de415b6e469c681701ea42d3b1c486fee31d6ef591a2d3e226f639ab486ff", - "assetStoreOriginalName": "Semi-transparent black and white (copy)", - "name": "Semi-transparent black and white (copy)", - "objects": [ - { - "assetStoreId": "", - "name": "NoButton", - "type": "PanelSpriteButton::PanelSpriteButton", - "variant": "Semi-transparent black and white", - "variables": [ - { - "name": "ID", - "type": "number", - "value": 0 - } - ], - "effects": [ - { - "effectType": "Outline", - "name": "OutlineEffect", - "doubleParameters": { - "padding": 0, - "thickness": 2 - }, - "stringParameters": { - "color": "255;255;255" - }, - "booleanParameters": {} - } - ], - "behaviors": [ - { - "name": "Anchor", - "type": "AnchorBehavior::AnchorBehavior", - "bottomEdgeAnchor": 2, - "leftEdgeAnchor": 0, - "relativeToOriginalWindowSize": true, - "rightEdgeAnchor": 2, - "topEdgeAnchor": 0, - "useLegacyBottomAndRightAnchors": false - } - ], - "content": { - "PressedLabelOffsetY": 0, - "LabelText": "No" - } - }, - { - "assetStoreId": "", - "name": "YesButton", - "type": "PanelSpriteButton::PanelSpriteButton", - "variant": "Semi-transparent black and white", - "variables": [ - { - "name": "ID", - "type": "number", - "value": 0 - } - ], - "effects": [ - { - "effectType": "Outline", - "name": "OutlineEffect", - "doubleParameters": { - "padding": 0, - "thickness": 2 - }, - "stringParameters": { - "color": "255;255;255" - }, - "booleanParameters": {} - } - ], - "behaviors": [ - { - "name": "Anchor", - "type": "AnchorBehavior::AnchorBehavior", - "bottomEdgeAnchor": 2, - "leftEdgeAnchor": 0, - "relativeToOriginalWindowSize": true, - "rightEdgeAnchor": 2, - "topEdgeAnchor": 0, - "useLegacyBottomAndRightAnchors": false - } - ], - "content": { - "PressedLabelOffsetY": 0, - "LabelText": "Yes" + } + }, + { + "assetStoreId": "", + "bottomMargin": 18, + "height": 48, + "leftMargin": 18, + "name": "TextBorder", + "persistentUuid": "0b929a4e-9454-4fb6-8576-a41a124addb9", + "rightMargin": 18, + "texture": "Border.png", + "tiled": true, + "topMargin": 18, + "type": "PanelSpriteObject::PanelSprite", + "width": 48, + "variables": [], + "effects": [], + "behaviors": [ + { + "name": "Anchor", + "type": "AnchorBehavior::AnchorBehavior", + "bottomEdgeAnchor": 2, + "leftEdgeAnchor": 1, + "relativeToOriginalWindowSize": true, + "rightEdgeAnchor": 2, + "topEdgeAnchor": 1, + "useLegacyBottomAndRightAnchors": false + } + ] + }, + { + "assetStoreId": "", + "name": "Dialogue", + "type": "BitmapText::BitmapTextObject", + "variables": [], + "effects": [], + "behaviors": [ + { + "name": "Anchor", + "type": "AnchorBehavior::AnchorBehavior", + "bottomEdgeAnchor": 2, + "leftEdgeAnchor": 1, + "relativeToOriginalWindowSize": true, + "rightEdgeAnchor": 2, + "topEdgeAnchor": 1, + "useLegacyBottomAndRightAnchors": false + } + ], + "content": { + "text": "Default message. Continue?", + "opacity": 255, + "scale": 2, + "fontSize": 20, + "tint": "255;255;255", + "bitmapFontResourceName": "8BitOperatorPlus8Regular_Edited.fnt", + "textureAtlasResourceName": "8BitOperatorPlus8Regular_Edited.png", + "align": "left", + "wordWrap": true, + "verticalTextAlignment": "top" + } + } + ], + "objectsFolderStructure": { + "folderName": "__ROOT", + "children": [ + { + "objectName": "Dialogue" + }, + { + "objectName": "TextBorder" + }, + { + "objectName": "YesButton" + }, + { + "objectName": "NoButton" + } + ] + }, + "objectsGroups": [ + { + "name": "Buttons", + "objects": [ + { + "name": "NoButton" + }, + { + "name": "YesButton" + } + ] + } + ], + "layers": [ + { + "ambientLightColorB": 200, + "ambientLightColorG": 200, + "ambientLightColorR": 200, + "camera2DPlaneMaxDrawingDistance": 5000, + "camera3DFarPlaneDistance": 10000, + "camera3DFieldOfView": 45, + "camera3DNearPlaneDistance": 3, + "cameraType": "", + "followBaseLayerCamera": false, + "isLightingLayer": false, + "isLocked": false, + "name": "", + "renderingType": "", + "visibility": true, + "cameras": [ + { + "defaultSize": true, + "defaultViewport": true, + "height": 0, + "viewportBottom": 1, + "viewportLeft": 0, + "viewportRight": 1, + "viewportTop": 0, + "width": 0 + } + ], + "effects": [] + } + ], + "instances": [ + { + "angle": 0, + "customSize": true, + "depth": 1, + "height": 124, + "layer": "", + "name": "Dialogue", + "persistentUuid": "c7183394-9ca6-4c16-9d13-f0b7cfe803d8", + "width": 554, + "x": 19, + "y": 18, + "zOrder": 45, + "numberProperties": [], + "stringProperties": [], + "initialVariables": [] + }, + { + "angle": 0, + "customSize": true, + "depth": 0, + "height": 213, + "layer": "", + "name": "TextBorder", + "persistentUuid": "639d5661-817d-4faa-b33b-c0ff129c309a", + "width": 600, + "x": 0, + "y": 0, + "zOrder": 43, + "numberProperties": [], + "stringProperties": [], + "initialVariables": [] + }, + { + "angle": 0, + "customSize": true, + "depth": 32, + "height": 42, + "layer": "", + "name": "YesButton", + "persistentUuid": "a0ca78d3-0a23-4bbe-89de-fb2d0a53996c", + "width": 105, + "x": 350, + "y": 154, + "zOrder": 510, + "numberProperties": [], + "stringProperties": [], + "initialVariables": [] + }, + { + "angle": 0, + "customSize": true, + "depth": 32, + "height": 42, + "layer": "", + "name": "NoButton", + "persistentUuid": "6781aafa-3ae9-4820-bb97-682a524a0cf6", + "width": 103, + "x": 471, + "y": 154, + "zOrder": 510, + "numberProperties": [], + "stringProperties": [], + "initialVariables": [ + { + "name": "ID", + "persistentUuid": "bb563dbe-93eb-4412-b843-f60016b9a893", + "type": "number", + "value": 1 + } + ] + } + ], + "editionSettings": { + "grid": false, + "gridType": "rectangular", + "gridWidth": 32, + "gridHeight": 32, + "gridDepth": 32, + "gridOffsetX": 0, + "gridOffsetY": 0, + "gridOffsetZ": 0, + "gridColor": 10401023, + "gridAlpha": 0.8, + "snap": false, + "zoomFactor": 1.1286095791267934, + "windowMask": false, + "selectedLayer": "", + "gameEditorMode": "instances-editor" + } + }, + { + "areaMaxX": 600, + "areaMaxY": 213, + "areaMaxZ": 32, + "areaMinX": 0, + "areaMinY": 0, + "areaMinZ": 0, + "assetStoreAssetId": "9d5de415b6e469c681701ea42d3b1c486fee31d6ef591a2d3e226f639ab486ff", + "assetStoreOriginalName": "Semi-transparent black and white (copy)", + "name": "Semi-transparent black and white (copy)", + "objects": [ + { + "assetStoreId": "", + "name": "NoButton", + "type": "PanelSpriteButton::PanelSpriteButton", + "variant": "Semi-transparent black and white", + "variables": [ + { + "name": "ID", + "type": "number", + "value": 0 + } + ], + "effects": [ + { + "effectType": "Outline", + "name": "OutlineEffect", + "doubleParameters": { + "padding": 0, + "thickness": 2 + }, + "stringParameters": { + "color": "255;255;255" + }, + "booleanParameters": {} + } + ], + "behaviors": [ + { + "name": "Anchor", + "type": "AnchorBehavior::AnchorBehavior", + "bottomEdgeAnchor": 2, + "leftEdgeAnchor": 0, + "relativeToOriginalWindowSize": true, + "rightEdgeAnchor": 2, + "topEdgeAnchor": 0, + "useLegacyBottomAndRightAnchors": false + } + ], + "content": { + "PressedLabelOffsetY": 0, + "LabelText": "No" + } + }, + { + "assetStoreId": "", + "name": "YesButton", + "type": "PanelSpriteButton::PanelSpriteButton", + "variant": "Semi-transparent black and white", + "variables": [ + { + "name": "ID", + "type": "number", + "value": 0 + } + ], + "effects": [ + { + "effectType": "Outline", + "name": "OutlineEffect", + "doubleParameters": { + "padding": 0, + "thickness": 2 + }, + "stringParameters": { + "color": "255;255;255" + }, + "booleanParameters": {} + } + ], + "behaviors": [ + { + "name": "Anchor", + "type": "AnchorBehavior::AnchorBehavior", + "bottomEdgeAnchor": 2, + "leftEdgeAnchor": 0, + "relativeToOriginalWindowSize": true, + "rightEdgeAnchor": 2, + "topEdgeAnchor": 0, + "useLegacyBottomAndRightAnchors": false + } + ], + "content": { + "PressedLabelOffsetY": 0, + "LabelText": "Yes" } }, { @@ -22951,20 +23171,22 @@ }, { "assetStoreId": "", - "bottomMargin": 11, + "bottomMargin": 18, "height": 106, - "leftMargin": 11, + "leftMargin": 18, "name": "Idle", - "rightMargin": 11, + "persistentUuid": "254dcfa6-6139-4367-9749-91132f41bef0", + "rightMargin": 18, "texture": "Border.png", "tiled": false, - "topMargin": 11, + "topMargin": 18, "type": "PanelSpriteObject::PanelSprite", "width": 256, "variables": [ { "folded": true, "name": "State", + "persistentUuid": "b562fb1e-48a7-46f2-8f48-a01125097603", "type": "string", "value": "Idle" } @@ -22990,14 +23212,15 @@ }, { "assetStoreId": "", - "bottomMargin": 8, + "bottomMargin": 18, "height": 106, - "leftMargin": 8, + "leftMargin": 18, "name": "Hovered", - "rightMargin": 8, + "persistentUuid": "ddd20d6d-ec1f-458c-be84-6f19c9377c7a", + "rightMargin": 18, "texture": "Border2.png", "tiled": false, - "topMargin": 8, + "topMargin": 18, "type": "PanelSpriteObject::PanelSprite", "width": 256, "variables": [], @@ -23021,14 +23244,15 @@ }, { "assetStoreId": "", - "bottomMargin": 8, + "bottomMargin": 18, "height": 106, - "leftMargin": 8, + "leftMargin": 18, "name": "Pressed", - "rightMargin": 8, + "persistentUuid": "1bfe3758-0c26-49b9-ac16-258afb377443", + "rightMargin": 18, "texture": "Border2.png", "tiled": false, - "topMargin": 8, + "topMargin": 18, "type": "PanelSpriteObject::PanelSprite", "width": 256, "variables": [], @@ -23199,7 +23423,23 @@ "initialVariables": [] } ], - "editionSettings": {} + "editionSettings": { + "grid": false, + "gridType": "rectangular", + "gridWidth": 32, + "gridHeight": 32, + "gridDepth": 32, + "gridOffsetX": 0, + "gridOffsetY": 0, + "gridOffsetZ": 0, + "gridColor": 10401023, + "gridAlpha": 0.8, + "snap": false, + "zoomFactor": 2.734375, + "windowMask": false, + "selectedLayer": "", + "gameEditorMode": "instances-editor" + } }, { "areaMaxX": 256, @@ -35008,6 +35248,11147 @@ } ], "eventsBasedObjects": [] + }, + { + "author": "", + "category": "Visual effect", + "dimension": "3D", + "extensionNamespace": "", + "fullName": "3D particle emitter", + "gdevelopVersion": ">=5.5.222", + "helpPath": "", + "iconUrl": "data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iVVRGLTgiPz48IURPQ1RZUEUgc3ZnIFBVQkxJQyAiLS8vVzNDLy9EVEQgU1ZHIDEuMS8vRU4iICJodHRwOi8vd3d3LnczLm9yZy9HcmFwaGljcy9TVkcvMS4xL0RURC9zdmcxMS5kdGQiPjxzdmcgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIiB4bWxuczp4bGluaz0iaHR0cDovL3d3dy53My5vcmcvMTk5OS94bGluayIgdmVyc2lvbj0iMS4xIiBpZD0ibWRpLWZpcmUiIHdpZHRoPSIyNCIgaGVpZ2h0PSIyNCIgdmlld0JveD0iMCAwIDI0IDI0Ij48cGF0aCBkPSJNMTcuNjYgMTEuMkMxNy40MyAxMC45IDE3LjE1IDEwLjY0IDE2Ljg5IDEwLjM4QzE2LjIyIDkuNzggMTUuNDYgOS4zNSAxNC44MiA4LjcyQzEzLjMzIDcuMjYgMTMgNC44NSAxMy45NSAzQzEzIDMuMjMgMTIuMTcgMy43NSAxMS40NiA0LjMyQzguODcgNi40IDcuODUgMTAuMDcgOS4wNyAxMy4yMkM5LjExIDEzLjMyIDkuMTUgMTMuNDIgOS4xNSAxMy41NUM5LjE1IDEzLjc3IDkgMTMuOTcgOC44IDE0LjA1QzguNTcgMTQuMTUgOC4zMyAxNC4wOSA4LjE0IDEzLjkzQzguMDggMTMuODggOC4wNCAxMy44MyA4IDEzLjc2QzYuODcgMTIuMzMgNi42OSAxMC4yOCA3LjQ1IDguNjRDNS43OCAxMCA0Ljg3IDEyLjMgNSAxNC40N0M1LjA2IDE0Ljk3IDUuMTIgMTUuNDcgNS4yOSAxNS45N0M1LjQzIDE2LjU3IDUuNyAxNy4xNyA2IDE3LjdDNy4wOCAxOS40MyA4Ljk1IDIwLjY3IDEwLjk2IDIwLjkyQzEzLjEgMjEuMTkgMTUuMzkgMjAuOCAxNy4wMyAxOS4zMkMxOC44NiAxNy42NiAxOS41IDE1IDE4LjU2IDEyLjcyTDE4LjQzIDEyLjQ2QzE4LjIyIDEyIDE3LjY2IDExLjIgMTcuNjYgMTEuMk0xNC41IDE3LjVDMTQuMjIgMTcuNzQgMTMuNzYgMTggMTMuNCAxOC4xQzEyLjI4IDE4LjUgMTEuMTYgMTcuOTQgMTAuNSAxNy4yOEMxMS42OSAxNyAxMi40IDE2LjEyIDEyLjYxIDE1LjIzQzEyLjc4IDE0LjQzIDEyLjQ2IDEzLjc3IDEyLjMzIDEzQzEyLjIxIDEyLjI2IDEyLjIzIDExLjYzIDEyLjUgMTAuOTRDMTIuNjkgMTEuMzIgMTIuODkgMTEuNyAxMy4xMyAxMkMxMy45IDEzIDE1LjExIDEzLjQ0IDE1LjM3IDE0LjhDMTUuNDEgMTQuOTQgMTUuNDMgMTUuMDggMTUuNDMgMTUuMjNDMTUuNDYgMTYuMDUgMTUuMSAxNi45NSAxNC41IDE3LjVIMTQuNVoiIC8+PC9zdmc+", + "name": "ParticleEmitter3D", + "previewIconUrl": "https://asset-resources.gdevelop.io/public-resources/Icons/f2e5a34bf465f781866677762d385d6c8e9e8d203383f2df9a3b7e0fad6a2cb5_fire.svg", + "shortDescription": "Display a large number of particles in 3D to create visual effects in a 3D game.", + "version": "3.1.0", + "description": [ + "3D particle emitters let you create and display many small particles to simulate visual effects in your game — like fire, explosions, smoke, or dust.", + "", + "The parameters of the object can be configured in multiple different ways to create a specific desired effect. GDevelop will give a set of a pre-configured 3D particle emitters that you should try first.", + "", + "Use these 3D particle emitters in 3D games. For 2D games or particles that appear on a game's 2D user interface, use the 2D particle emitter object instead." + ], + "origin": { + "identifier": "ParticleEmitter3D", + "name": "gdevelop-extension-store" + }, + "tags": [ + "3d", + "particle", + "explosion", + "fire", + "smoke", + "splash", + "light" + ], + "authorIds": [ + "IWykYNRvhCZBN3vEgKEbBPOR3Oc2" + ], + "changelog": [ + { + "version": "3.0.0", + "breaking": "- the object rotates the other way around X axis." + }, + { + "version": "2.0.0", + "breaking": "- Object properties for position and rotation have been removed. They must be set with the instance editor or the action." + }, + { + "version": "1.0.0", + "breaking": "- Particles were 3 times too small." + } + ], + "dependencies": [], + "globalVariables": [], + "sceneVariables": [], + "eventsFunctions": [ + { + "description": "Define helper classes JavaScript code.", + "fullName": "Define helper classes", + "functionType": "Action", + "name": "DefineHelperClasses", + "private": true, + "sentence": "Define helper classes JavaScript code", + "events": [ + { + "type": "BuiltinCommonInstructions::JsCode", + "inlineCode": [ + "if (gdjs.__particleEmmiter3DExtension) {", + " return;", + "}", + "", + "const game = runtimeScene.getGame();", + "", + "class ParticleEmitter3DRenderer extends gdjs.CustomRuntimeObject3DRenderer {", + " constructor(", + " object,", + " instanceContainer,", + " parent", + " ) {", + " super(object, instanceContainer, parent);", + " }", + "", + " _updateThreeGroup() {", + " const threeObject3D = this.get3DRendererObject();", + "", + " threeObject3D.rotation.set(", + " gdjs.toRad(this._object.getRotationX()),", + " gdjs.toRad(this._object.getRotationY()),", + " gdjs.toRad(this._object.angle)", + " );", + "", + " threeObject3D.position.set(", + " this._object.getX(),", + " this._object.getY(),", + " this._object.getZ()", + " );", + "", + " // Force the scale to 1 because the particle emitter doesn't really has a size.", + " threeObject3D.scale.set(", + " this._object.isFlippedX() ? -1 : 1,", + " this._object.isFlippedY() ? -1 : 1,", + " this._object.isFlippedZ() ? -1 : 1", + " );", + "", + " threeObject3D.visible = !this._object.hidden;", + "", + " this._isContainerDirty = true;", + " }", + "}", + "", + "const coneLength = 64;", + "", + "class ParticleEmitterHelper extends THREE.Object3D {", + " /** @type {gdjs.CustomRuntimeObject3D} */", + " object;", + " /** @type {THREE.LineSegments} */", + " cone;", + " /** @type {THREE.LineSegments} */", + " centerLine;", + " /** @type {THREE.Mesh} */", + " originBox;", + "", + "\t/**", + " * @param gdjs.CustomRuntimeObject3D object", + "\t */", + " constructor(object) {", + " super();", + " this.object = object;", + " this.type = 'ParticleEmitterHelper';", + "", + " const coneGeometry = new THREE.BufferGeometry();", + " const conePositions = [", + " 0, 0, 0, 0, 0, 1,", + " 0, 0, 0, 1, 0, 1,", + " 0, 0, 0, - 1, 0, 1,", + " 0, 0, 0, 0, 1, 1,", + " 0, 0, 0, 0, - 1, 1", + " ];", + " for (let i = 0, j = 1, l = 32; i < l; i++ , j++) {", + " const p1 = (i / l) * Math.PI * 2;", + " const p2 = (j / l) * Math.PI * 2;", + " conePositions.push(", + " Math.cos(p1), Math.sin(p1), 1,", + " Math.cos(p2), Math.sin(p2), 1", + " );", + " }", + " coneGeometry.setAttribute('position', new THREE.Float32BufferAttribute(conePositions, 3));", + " this.cone = new THREE.LineSegments(", + " coneGeometry,", + " new THREE.LineBasicMaterial({ fog: false, toneMapped: false })", + " );", + " this.add(this.cone);", + " const centerLineGeometry = new THREE.BufferGeometry();", + " centerLineGeometry.setAttribute('position', new THREE.Float32BufferAttribute([", + " 0, 0, 0, 0, 0, coneLength", + " ], 3));", + " this.centerLine = new THREE.LineSegments(", + " centerLineGeometry,", + " new THREE.LineBasicMaterial({ fog: false, toneMapped: false })", + " );", + " this.add(this.centerLine);", + "", + " this.originBox = new THREE.Mesh(", + " new THREE.BoxGeometry(16, 16, 16),", + " new THREE.MeshBasicMaterial({ fog: false, toneMapped: false })", + " );", + " this.add(this.originBox);", + " this.update();", + "", + " this.originBox.gdjsRuntimeObject = object;", + " }", + "", + " dispose() {", + " this.cone.geometry.dispose();", + " this.cone.material.dispose();", + " this.centerLine.geometry.dispose();", + " this.centerLine.material.dispose();", + " this.originBox.geometry.dispose();", + " this.originBox.material.dispose();", + " }", + "", + " update() {", + " const coneWidth = coneLength * Math.sin(gdjs.toRad(this.object._getSpayConeAngle()));", + " const coneHeight = coneLength * Math.cos(gdjs.toRad(this.object._getSpayConeAngle()));", + " this.cone.scale.set(coneWidth, coneWidth, coneHeight);", + " const startColor = gdjs.rgbOrHexStringToNumber(this.object._getStartColor())", + " const endColor = gdjs.rgbOrHexStringToNumber(this.object._getEndColor());", + " this.cone.material.color.set(endColor);", + " this.centerLine.material.color.set(startColor);", + " this.originBox.material.color.set(startColor);", + " }", + "}", + "", + "/**", + " * @param {string} colorString", + " * @param {THREE.Vector4} threeColor", + " */", + "const setThreeColor = (colorString, threeColor = new THREE.Vector4()) => {", + " const integerColor = gdjs.rgbOrHexToRGBColor(colorString);", + " threeColor.x = integerColor[0] / 255;", + " threeColor.y = integerColor[1] / 255;", + " threeColor.z = integerColor[2] / 255;", + "};", + "", + "/**", + " * @param {string} integerOpacity", + " * @param {THREE.Vector4} threeColor", + " */", + "const setThreeOpacity = (integerOpacity, threeColor = new THREE.Vector4()) => {", + " threeColor.w = integerOpacity / 255;", + "};", + "", + "class ParticleEmitterAdapter {", + " /**", + " * @param particleSystem {ParticleSystem}", + " * @param colorOverLife {ColorOverLife}", + " * @param sizeOverLife {SizeOverLife}", + " * @param applyForce {ApplyForce}", + " * @param applyForce {ApplyForce}", + " * @param widthOverLength {WidthOverLength}", + " */", + " constructor(particleSystem, colorOverLife, sizeOverLife, applyForce, widthOverLength) {", + " this.particleSystem = particleSystem;", + " this.colorOverLife = colorOverLife;", + " this.sizeOverLife = sizeOverLife;", + " this.applyForce = applyForce;", + " this.widthOverLength = widthOverLength;", + " }", + "", + " /**", + " * @param renderMode {'Billboard' | 'Trail'}", + " */", + " setRenderMode(renderMode) {", + " switch (renderMode) {", + " case \"BillBoard\":", + " this.particleSystem.renderMode = RenderMode.BillBoard;", + " break;", + " case \"Trail\":", + " this.particleSystem.renderMode = RenderMode.Trail;", + " this.particleSystem.rendererEmitterSettings.startLength = new IntervalValue(30, 30);", + " break;", + " }", + " }", + "", + " /**", + " * @param startLengthMin {number}", + " */", + " setTrailStartLengthMin(startLengthMin) {", + " const startLength = this.particleSystem.rendererEmitterSettings.startLength;", + " if (!startLength) {", + " return;", + " }", + " startLength.a = startLengthMin;", + " }", + "", + " /**", + " * @param startLengthMax {number}", + " */", + " setTrailStartLengthMax(startLengthMax) {", + " const startLength = this.particleSystem.rendererEmitterSettings.startLength;", + " if (!startLength) {", + " return;", + " }", + " startLength.b = startLengthMax;", + " }", + "", + " /**", + " * @param followLocalOrigin {boolean}", + " */", + " setTrailFollowingLocalOrigin(followLocalOrigin) {", + " this.particleSystem.rendererEmitterSettings.followLocalOrigin = followLocalOrigin;", + " }", + "", + " /**", + " * @param trailEndWidthRatio {number}", + " */", + " setTrailEndWidthRatio(trailEndWidthRatio) {", + " // Check this comment in the lib code:", + " // We added a multiplication with size because it's more convenient for the extension.", + "", + " /** @type Bezier */", + " const bezier = this.widthOverLength.width.functions[0][0];", + " bezier.p[0] = 1;", + " bezier.p[1] = gdjs.evtTools.common.lerp(1, trailEndWidthRatio, 1 / 3);", + " bezier.p[2] = gdjs.evtTools.common.lerp(1, trailEndWidthRatio, 2 / 3);", + " bezier.p[3] = trailEndWidthRatio;", + " }", + "", + "", + " /**", + " * @param startColor {string}", + " */", + " setStartColor(startColor) {", + " setThreeColor(startColor, this.colorOverLife.color.color.keys[0][0]);", + " }", + "", + " /**", + " * @param endColor {string}", + " */", + " setEndColor(endColor) {", + " setThreeColor(endColor, this.colorOverLife.color.color.keys[1][0]);", + " }", + "", + " /**", + " * @param startOpacity {number}", + " */", + " setStartOpacity(startOpacity) {", + " this.colorOverLife.color.alpha.keys[0][0] = startOpacity / 255;", + " }", + "", + " /**", + " * @param endOpacity {number}", + " */", + " setEndOpacity(endOpacity) {", + " this.colorOverLife.color.alpha.keys[1][0] = endOpacity / 255;", + " }", + "", + " /**", + " * @param flow {number}", + " */", + " setFlow(flow) {", + " this.particleSystem.emissionOverTime.value = flow;", + " }", + "", + " /**", + " * @param startMinSize {number}", + " */", + " setStartMinSize(startMinSize) {", + " this.particleSystem.startSize.a = startMinSize;", + " }", + "", + " /**", + " * @param startMaxSize {number}", + " */", + " setStartMaxSize(startMaxSize) {", + " this.particleSystem.startSize.b = startMaxSize;", + " }", + "", + " /**", + " * @param endScale {number}", + " */", + " setEndScale(endScale) {", + " /** @type Bezier */", + " const bezier = this.sizeOverLife.size.functions[0][0];", + " bezier.p[0] = 1;", + " bezier.p[1] = gdjs.evtTools.common.lerp(1, endScale, 1 / 3);", + " bezier.p[2] = gdjs.evtTools.common.lerp(1, endScale, 2 / 3);", + " bezier.p[3] = endScale;", + " }", + "", + " /**", + " * @param startSpeedMin {number}", + " */", + " setStartSpeedMin(startSpeedMin) {", + " this.particleSystem.startSpeed.a = startSpeedMin;", + " }", + "", + " /**", + " * @param startSpeedMax {number}", + " */", + " setStartSpeedMax(startSpeedMax) {", + " this.particleSystem.startSpeed.b = startSpeedMax;", + " }", + "", + " /**", + " * @param lifespanMin {number}", + " */", + " setLifespanMin(lifespanMin) {", + " this.particleSystem.startLife.a = lifespanMin;", + " }", + "", + " /**", + " * @param lifespanMax {number}", + " */", + " setLifespanMax(lifespanMax) {", + " this.particleSystem.startLife.b = lifespanMax;", + " }", + "", + " /**", + " * @param duration {number}", + " */", + " setDuration(duration) {", + " this.particleSystem.duration = duration || Number.POSITIVE_INFINITY;", + " }", + "", + " /**", + " * @param areParticlesRelative {boolean}", + " */", + " setParticlesRelative(areParticlesRelative) {", + " this.particleSystem.worldSpace = !areParticlesRelative;", + " }", + "", + " /**", + " * @param sprayConeAngle {number}", + " */", + " setSprayConeAngle(sprayConeAngle) {", + " if (0 < sprayConeAngle && sprayConeAngle <= 180) {", + " if (!this.particleSystem.emitterShape.angle) {", + " this.particleSystem.emitterShape = new ConeEmitter({ radius: 0.1, angle: Math.PI / 8 });", + " }", + " this.particleSystem.emitterShape.angle = sprayConeAngle * Math.PI / 180;", + " }", + " else {", + " if (this.particleSystem.emitterShape.angle) {", + " this.particleSystem.emitterShape = new PointEmitter();", + " }", + " }", + " }", + "", + " /**", + " * @param blending {string}", + " */", + " setBlending(blendingString) {", + " const blending =", + " blendingString === \"Additive\" ? THREE.AdditiveBlending :", + " blendingString === \"Normal\" ? THREE.NormalBlending :", + " blendingString === \"Subtractive\" ? THREE.SubtractiveBlending :", + " blendingString === \"Multiply\" ? THREE.MultiplyBlending :", + " blendingString === \"None\" ? THREE.NoBlending :", + " THREE.AdditiveBlending;", + " // TODO This doesn't work.", + " this.particleSystem.blending = blending;", + " }", + "", + " /**", + " * @param gravity {number}", + " */", + " setGravity(gravity) {", + " this.applyForce.magnitude.value = gravity;", + " }", + "", + " /**", + " * @param gravityTop {string}", + " */", + " setGravityTop(gravityTop) {", + " // TODO Make gravity absolute even when \"relative\" is enabled. ", + " switch (gravityTop) {", + " case \"Z+\":", + " this.applyForce.direction.set(0, 0, -1);", + " break;", + "", + " case \"Y-\":", + " this.applyForce.direction.set(0, 1, 0);", + " break;", + " }", + " }", + "", + " setImage(resourceName) {", + " const texture = game.getImageManager().getThreeTexture(resourceName);", + " this.particleSystem.texture = texture;", + " }", + "}", + "", + "", + "", + "/**", + " * three.quarks v0.11.2 build Mon Jan 22 2024", + " * https://github.com/Alchemist0823/three.quarks#readme", + " * Copyright 2024 Alchemist0823 , MIT", + " */", + "", + "// The migration to addUpdateRange was backported without updating the library", + "// because GDevelop still uses Three.js 0.160.0.", + "", + "class ParticleEmitter extends THREE.Object3D {", + " constructor(system) {", + " super();", + " this.type = 'ParticleEmitter';", + " this.system = system;", + " }", + " clone() {", + " const system = this.system.clone();", + " system.emitter.copy(this, true);", + " return system.emitter;", + " }", + " dispose() { }", + " extractFromCache(cache) {", + " const values = [];", + " for (const key in cache) {", + " const data = cache[key];", + " delete data.metadata;", + " values.push(data);", + " }", + " return values;", + " }", + " toJSON(meta, options = {}) {", + " const isRootObject = meta === undefined || typeof meta === 'string';", + " const output = {};", + " if (isRootObject) {", + " meta = {", + " geometries: {},", + " materials: {},", + " textures: {},", + " images: {},", + " shapes: {},", + " skeletons: {},", + " animations: {},", + " nodes: {},", + " };", + " output.metadata = {", + " version: 4.5,", + " type: 'Object',", + " generator: 'Object3D.toJSON',", + " };", + " }", + " const object = {};", + " object.uuid = this.uuid;", + " object.type = this.type;", + " if (this.name !== '')", + " object.name = this.name;", + " if (this.castShadow === true)", + " object.castShadow = true;", + " if (this.receiveShadow === true)", + " object.receiveShadow = true;", + " if (this.visible === false)", + " object.visible = false;", + " if (this.frustumCulled === false)", + " object.frustumCulled = false;", + " if (this.renderOrder !== 0)", + " object.renderOrder = this.renderOrder;", + " if (JSON.stringify(this.userData) !== '{}')", + " object.userData = this.userData;", + " object.layers = this.layers.mask;", + " object.matrix = this.matrix.toArray();", + " if (this.matrixAutoUpdate === false)", + " object.matrixAutoUpdate = false;", + " if (this.system !== null)", + " object.ps = this.system.toJSON(meta, options);", + " if (this.children.length > 0) {", + " object.children = [];", + " for (let i = 0; i < this.children.length; i++) {", + " if (this.children[i].type !== 'ParticleSystemPreview') {", + " object.children.push(this.children[i].toJSON(meta).object);", + " }", + " }", + " }", + " if (isRootObject) {", + " const geometries = this.extractFromCache(meta.geometries);", + " const materials = this.extractFromCache(meta.materials);", + " const textures = this.extractFromCache(meta.textures);", + " const images = this.extractFromCache(meta.images);", + " if (geometries.length > 0)", + " output.geometries = geometries;", + " if (materials.length > 0)", + " output.materials = materials;", + " if (textures.length > 0)", + " output.textures = textures;", + " if (images.length > 0)", + " output.images = images;", + " }", + " output.object = object;", + " return output;", + " }", + "}", + "", + "class LinkedListNode {", + " constructor(data) {", + " this.data = data;", + " this.next = null;", + " this.prev = null;", + " }", + " hasPrev() {", + " return this.prev !== null;", + " }", + " hasNext() {", + " return this.next !== null;", + " }", + "}", + "class LinkedList {", + " constructor() {", + " this.length = 0;", + " this.head = this.tail = null;", + " }", + " isEmpty() {", + " return this.head === null;", + " }", + " clear() {", + " this.length = 0;", + " this.head = this.tail = null;", + " }", + " front() {", + " if (this.head === null)", + " return null;", + " return this.head.data;", + " }", + " back() {", + " if (this.tail === null)", + " return null;", + " return this.tail.data;", + " }", + " dequeue() {", + " if (this.head) {", + " const value = this.head.data;", + " this.head = this.head.next;", + " if (!this.head) {", + " this.tail = null;", + " }", + " else {", + " this.head.prev = null;", + " }", + " this.length--;", + " return value;", + " }", + " return undefined;", + " }", + " pop() {", + " if (this.tail) {", + " const value = this.tail.data;", + " this.tail = this.tail.prev;", + " if (!this.tail) {", + " this.head = null;", + " }", + " else {", + " this.tail.next = null;", + " }", + " this.length--;", + " return value;", + " }", + " return undefined;", + " }", + " queue(data) {", + " const node = new LinkedListNode(data);", + " if (!this.tail) {", + " this.tail = node;", + " }", + " if (this.head) {", + " this.head.prev = node;", + " node.next = this.head;", + " }", + " this.head = node;", + " this.length++;", + " }", + " push(data) {", + " const node = new LinkedListNode(data);", + " if (!this.head) {", + " this.head = node;", + " }", + " if (this.tail) {", + " this.tail.next = node;", + " node.prev = this.tail;", + " }", + " this.tail = node;", + " this.length++;", + " }", + " insertBefore(node, data) {", + " const newNode = new LinkedListNode(data);", + " newNode.next = node;", + " newNode.prev = node.prev;", + " if (newNode.prev !== null) {", + " newNode.prev.next = newNode;", + " }", + " newNode.next.prev = newNode;", + " if (node == this.head) {", + " this.head = newNode;", + " }", + " this.length++;", + " }", + " remove(data) {", + " if (this.head === null || this.tail === null) {", + " return;", + " }", + " let tempNode = this.head;", + " if (data === this.head.data) {", + " this.head = this.head.next;", + " }", + " if (data === this.tail.data) {", + " this.tail = this.tail.prev;", + " }", + " while (tempNode.next !== null && tempNode.data !== data) {", + " tempNode = tempNode.next;", + " }", + " if (tempNode.data === data) {", + " if (tempNode.prev !== null)", + " tempNode.prev.next = tempNode.next;", + " if (tempNode.next !== null)", + " tempNode.next.prev = tempNode.prev;", + " this.length--;", + " }", + " }", + " *values() {", + " let current = this.head;", + " while (current !== null) {", + " yield current.data;", + " current = current.next;", + " }", + " }", + "}", + "", + "class NodeParticle {", + " constructor() {", + " this.position = new THREE.Vector3();", + " this.velocity = new THREE.Vector3();", + " this.age = 0;", + " this.life = 1;", + " this.size = 1;", + " this.rotation = 0;", + " this.color = new THREE.Vector4(1, 1, 1, 1);", + " this.uvTile = 0;", + " }", + " get died() {", + " return this.age >= this.life;", + " }", + " reset() {", + " this.position.set(0, 0, 0);", + " this.velocity.set(0, 0, 0);", + " this.age = 0;", + " this.life = 1;", + " this.size = 1;", + " this.rotation = 0;", + " this.color.set(1, 1, 1, 1);", + " this.uvTile = 0;", + " }", + "}", + "class SpriteParticle {", + " constructor() {", + " this.startSpeed = 0;", + " this.startColor = new THREE.Vector4();", + " this.startSize = 1;", + " this.position = new THREE.Vector3();", + " this.velocity = new THREE.Vector3();", + " this.age = 0;", + " this.life = 1;", + " this.size = 1;", + " this.speedModifier = 1;", + " this.rotation = 0;", + " this.color = new THREE.Vector4();", + " this.uvTile = 0;", + " }", + " get died() {", + " return this.age >= this.life;", + " }", + "}", + "class RecordState {", + " constructor(position, size, color) {", + " this.position = position;", + " this.size = size;", + " this.color = color;", + " }", + "}", + "class TrailParticle {", + " constructor() {", + " this.startSpeed = 0;", + " this.startColor = new THREE.Vector4();", + " this.startSize = 1;", + " this.position = new THREE.Vector3();", + " this.velocity = new THREE.Vector3();", + " this.age = 0;", + " this.life = 1;", + " this.size = 1;", + " this.length = 100;", + " this.speedModifier = 1;", + " this.color = new THREE.Vector4();", + " this.previous = new LinkedList();", + " this.uvTile = 0;", + " }", + " update() {", + " if (this.age <= this.life) {", + " this.previous.push(new RecordState(this.position.clone(), this.size, this.color.clone()));", + " }", + " else {", + " if (this.previous.length > 0) {", + " this.previous.dequeue();", + " }", + " }", + " while (this.previous.length > this.length) {", + " this.previous.dequeue();", + " }", + " }", + " get died() {", + " return this.age >= this.life;", + " }", + " reset() {", + " this.previous.clear();", + " }", + "}", + "", + "class Bezier {", + " constructor(p1, p2, p3, p4) {", + " this.p = [p1, p2, p3, p4];", + " }", + " genValue(t) {", + " const t2 = t * t;", + " const t3 = t * t * t;", + " const mt = 1 - t;", + " const mt2 = mt * mt;", + " const mt3 = mt2 * mt;", + " return this.p[0] * mt3 + this.p[1] * mt2 * t * 3 + this.p[2] * mt * t2 * 3 + this.p[3] * t3;", + " }", + " derivativeCoefficients(points) {", + " const dpoints = [];", + " for (let p = points, c = p.length - 1; c > 0; c--) {", + " const list = [];", + " for (let j = 0; j < c; j++) {", + " const dpt = c * (p[j + 1] - p[j]);", + " list.push(dpt);", + " }", + " dpoints.push(list);", + " p = list;", + " }", + " return dpoints;", + " }", + " getSlope(t) {", + " const p = this.derivativeCoefficients(this.p)[0];", + " const mt = 1 - t;", + " const a = mt * mt;", + " const b = mt * t * 2;", + " const c = t * t;", + " return a * p[0] + b * p[1] + c * p[2];", + " }", + " controlCurve(d0, d1) {", + " this.p[1] = d0 / 3 + this.p[0];", + " this.p[2] = this.p[3] - d1 / 3;", + " }", + " hull(t) {", + " let p = this.p;", + " let _p = [], pt, idx = 0, i = 0, l = 0;", + " const q = [];", + " q[idx++] = p[0];", + " q[idx++] = p[1];", + " q[idx++] = p[2];", + " q[idx++] = p[3];", + " while (p.length > 1) {", + " _p = [];", + " for (i = 0, l = p.length - 1; i < l; i++) {", + " pt = t * p[i] + (1 - t) * p[i + 1];", + " q[idx++] = pt;", + " _p.push(pt);", + " }", + " p = _p;", + " }", + " return q;", + " }", + " split(t) {", + " const q = this.hull(t);", + " const result = {", + " left: new Bezier(q[0], q[4], q[7], q[9]),", + " right: new Bezier(q[9], q[8], q[6], q[3]),", + " span: q", + " };", + " return result;", + " }", + " clone() {", + " return new Bezier(this.p[0], this.p[1], this.p[2], this.p[3]);", + " }", + " toJSON() {", + " return {", + " p0: this.p[0],", + " p1: this.p[1],", + " p2: this.p[2],", + " p3: this.p[3],", + " };", + " }", + " static fromJSON(json) {", + " return new Bezier(json.p0, json.p1, json.p2, json.p3);", + " }", + "}", + "", + "const ColorToJSON = (color) => {", + " return { r: color.x, g: color.y, b: color.z, a: color.w };", + "};", + "const JSONToColor = (json) => {", + " return new THREE.Vector4(json.r, json.g, json.b, json.a);", + "};", + "const JSONToValue = (json, type) => {", + " switch (type) {", + " case 'Vector3':", + " return new THREE.Vector3(json.x, json.y, json.z);", + " case 'Vector4':", + " return new THREE.Vector4(json.x, json.y, json.z, json.w);", + " case 'Color':", + " return new THREE.Vector3(json.r, json.g, json.b);", + " case 'Number':", + " return json;", + " default:", + " return json;", + " }", + "};", + "const ValueToJSON = (value, type) => {", + " switch (type) {", + " case 'Vector3':", + " return { x: value.x, y: value.y, z: value.z };", + " case 'Vector4':", + " return { x: value.x, y: value.y, z: value.z, w: value.w };", + " case 'Color':", + " return { r: value.x, g: value.y, b: value.z };", + " case 'Number':", + " return value;", + " default:", + " return value;", + " }", + "};", + "", + "class RandomColor {", + " constructor(a, b) {", + " this.a = a;", + " this.b = b;", + " this.type = \"value\";", + " }", + " genColor(color) {", + " const rand = Math.random();", + " return color.copy(this.a).lerp(this.b, rand);", + " }", + " toJSON() {", + " return {", + " type: \"RandomColor\",", + " a: ColorToJSON(this.a),", + " b: ColorToJSON(this.b),", + " };", + " }", + " static fromJSON(json) {", + " return new RandomColor(JSONToColor(json.a), JSONToColor(json.b));", + " }", + " clone() {", + " return new RandomColor(this.a.clone(), this.b.clone());", + " }", + "}", + "", + "class ColorRange {", + " constructor(a, b) {", + " this.a = a;", + " this.b = b;", + " this.type = 'value';", + " }", + " genColor(color, t) {", + " return color.copy(this.a).lerp(this.b, Math.random());", + " }", + " toJSON() {", + " return {", + " type: 'ColorRange',", + " a: ColorToJSON(this.a),", + " b: ColorToJSON(this.b),", + " };", + " }", + " static fromJSON(json) {", + " return new ColorRange(JSONToColor(json.a), JSONToColor(json.b));", + " }", + " clone() {", + " return new ColorRange(this.a.clone(), this.b.clone());", + " }", + "}", + "", + "class ContinuousLinearFunction {", + " constructor(keys, subType) {", + " this.subType = subType;", + " this.type = 'function';", + " this.keys = keys;", + " }", + " findKey(t) {", + " let mid = 0;", + " let left = 0, right = this.keys.length - 1;", + " while (left + 1 < right) {", + " mid = Math.floor((left + right) / 2);", + " if (t < this.getStartX(mid))", + " right = mid - 1;", + " else if (t > this.getEndX(mid))", + " left = mid + 1;", + " else", + " return mid;", + " }", + " for (let i = left; i <= right; i++) {", + " if (t >= this.getStartX(i) && t <= this.getEndX(i))", + " return i;", + " }", + " return -1;", + " }", + " getStartX(index) {", + " return this.keys[index][1];", + " }", + " getEndX(index) {", + " if (index + 1 < this.keys.length)", + " return this.keys[index + 1][1];", + " return 1;", + " }", + " genValue(value, t) {", + " const index = this.findKey(t);", + " if (this.subType === 'Number') {", + " if (index === -1) {", + " return this.keys[0][0];", + " }", + " else if (index + 1 >= this.keys.length) {", + " return this.keys[this.keys.length - 1][0];", + " }", + " return ((this.keys[index + 1][0] - this.keys[index][0]) *", + " ((t - this.getStartX(index)) / (this.getEndX(index) - this.getStartX(index))) +", + " this.keys[index][0]);", + " }", + " else {", + " if (index === -1) {", + " return value.copy(this.keys[0][0]);", + " }", + " if (index + 1 >= this.keys.length) {", + " return value.copy(this.keys[this.keys.length - 1][0]);", + " }", + " return value", + " .copy(this.keys[index][0])", + " .lerp(this.keys[index + 1][0], (t - this.getStartX(index)) / (this.getEndX(index) - this.getStartX(index)));", + " }", + " }", + " toJSON() {", + " this.keys[0][0].constructor.name;", + " return {", + " type: 'CLinearFunction',", + " subType: this.subType,", + " keys: this.keys.map(([color, pos]) => ({ value: ValueToJSON(color, this.subType), pos: pos })),", + " };", + " }", + " static fromJSON(json) {", + " return new ContinuousLinearFunction(json.keys.map((pair) => [JSONToValue(pair.value, json.subType), pair.pos]), json.subType);", + " }", + " clone() {", + " if (this.subType === 'Number') {", + " return new ContinuousLinearFunction(this.keys.map(([value, pos]) => [value, pos]), this.subType);", + " }", + " else {", + " return new ContinuousLinearFunction(this.keys.map(([value, pos]) => [value.clone(), pos]), this.subType);", + " }", + " }", + "}", + "", + "const tempVec3 = new THREE.Vector3();", + "class Gradient {", + " constructor(color = [", + " [new THREE.Vector3(0, 0, 0), 0],", + " [new THREE.Vector3(1, 1, 1), 0],", + " ], alpha = [", + " [1, 0],", + " [1, 1],", + " ]) {", + " this.type = 'function';", + " this.color = new ContinuousLinearFunction(color, 'Color');", + " this.alpha = new ContinuousLinearFunction(alpha, 'Number');", + " }", + " genColor(color, t) {", + " this.color.genValue(tempVec3, t);", + " return color.set(tempVec3.x, tempVec3.y, tempVec3.z, this.alpha.genValue(1, t));", + " }", + " toJSON() {", + " return {", + " type: 'Gradient',", + " color: this.color.toJSON(),", + " alpha: this.alpha.toJSON(),", + " };", + " }", + " static fromJSON(json) {", + " if (json.functions) {", + " const keys = json.functions.map((func) => [ColorRange.fromJSON(func.function).a, func.start]);", + " if (json.functions.length > 0) {", + " keys.push([ColorRange.fromJSON(json.functions[json.functions.length - 1].function).b, 1]);", + " }", + " return new Gradient(keys.map((key) => [new THREE.Vector3(key[0].x, key[0].y, key[0].z), key[1]]), keys.map((key) => [key[0].w, key[1]]));", + " }", + " else {", + " const gradient = new Gradient();", + " gradient.alpha = ContinuousLinearFunction.fromJSON(json.alpha);", + " gradient.color = ContinuousLinearFunction.fromJSON(json.color);", + " return gradient;", + " }", + " }", + " clone() {", + " const gradient = new Gradient();", + " gradient.alpha = this.alpha.clone();", + " gradient.color = this.color.clone();", + " return gradient;", + " }", + "}", + "", + "const tempColor = new THREE.Vector4();", + "class RandomColorBetweenGradient {", + " constructor(gradient1, gradient2) {", + " this.type = 'memorizedFunction';", + " this.gradient1 = gradient1;", + " this.gradient2 = gradient2;", + " }", + " startGen(memory) {", + " memory.rand = Math.random();", + " }", + " genColor(color, t, memory) {", + " this.gradient1.genColor(color, t);", + " this.gradient2.genColor(tempColor, t);", + " if (memory && memory.rand) {", + " color.lerp(tempColor, memory.rand);", + " }", + " else {", + " color.lerp(tempColor, Math.random());", + " }", + " return color;", + " }", + " toJSON() {", + " return {", + " type: 'RandomColorBetweenGradient',", + " gradient1: this.gradient1.toJSON(),", + " gradient2: this.gradient2.toJSON(),", + " };", + " }", + " static fromJSON(json) {", + " return new RandomColorBetweenGradient(Gradient.fromJSON(json.gradient1), Gradient.fromJSON(json.gradient2));", + " }", + " clone() {", + " return new RandomColorBetweenGradient(this.gradient1.clone(), this.gradient2.clone());", + " }", + "}", + "", + "class ConstantColor {", + " constructor(color) {", + " this.color = color;", + " this.type = 'value';", + " }", + " genColor(color) {", + " return color.copy(this.color);", + " }", + " toJSON() {", + " return {", + " type: 'ConstantColor',", + " color: ColorToJSON(this.color),", + " };", + " }", + " static fromJSON(json) {", + " return new ConstantColor(JSONToColor(json.color));", + " }", + " clone() {", + " return new ConstantColor(this.color.clone());", + " }", + "}", + "function ColorGeneratorFromJSON(json) {", + " switch (json.type) {", + " case 'ConstantColor':", + " return ConstantColor.fromJSON(json);", + " case 'ColorRange':", + " return ColorRange.fromJSON(json);", + " case 'RandomColor':", + " return RandomColor.fromJSON(json);", + " case 'Gradient':", + " return Gradient.fromJSON(json);", + " case 'RandomColorBetweenGradient':", + " return RandomColorBetweenGradient.fromJSON(json);", + " default:", + " return new ConstantColor(new THREE.Vector4(1, 1, 1, 1));", + " }", + "}", + "", + "class ConstantValue {", + " constructor(value) {", + " this.value = value;", + " this.type = 'value';", + " }", + " genValue() {", + " return this.value;", + " }", + " toJSON() {", + " return {", + " type: \"ConstantValue\",", + " value: this.value", + " };", + " }", + " static fromJSON(json) {", + " return new ConstantValue(json.value);", + " }", + " clone() {", + " return new ConstantValue(this.value);", + " }", + "}", + "", + "class IntervalValue {", + " constructor(a, b) {", + " this.a = a;", + " this.b = b;", + " this.type = 'value';", + " }", + " genValue() {", + " return THREE.MathUtils.lerp(this.a, this.b, Math.random());", + " }", + " toJSON() {", + " return {", + " type: 'IntervalValue',", + " a: this.a,", + " b: this.b,", + " };", + " }", + " static fromJSON(json) {", + " return new IntervalValue(json.a, json.b);", + " }", + " clone() {", + " return new IntervalValue(this.a, this.b);", + " }", + "}", + "", + "class PiecewiseFunction {", + " constructor() {", + " this.functions = new Array();", + " }", + " findFunction(t) {", + " let mid = 0;", + " let left = 0, right = this.functions.length - 1;", + " while (left + 1 < right) {", + " mid = Math.floor((left + right) / 2);", + " if (t < this.getStartX(mid))", + " right = mid - 1;", + " else if (t > this.getEndX(mid))", + " left = mid + 1;", + " else", + " return mid;", + " }", + " for (let i = left; i <= right; i++) {", + " if (t >= this.functions[i][1] && t <= this.getEndX(i))", + " return i;", + " }", + " return -1;", + " }", + " getStartX(index) {", + " return this.functions[index][1];", + " }", + " setStartX(index, x) {", + " if (index > 0)", + " this.functions[index][1] = x;", + " }", + " getEndX(index) {", + " if (index + 1 < this.functions.length)", + " return this.functions[index + 1][1];", + " return 1;", + " }", + " setEndX(index, x) {", + " if (index + 1 < this.functions.length)", + " this.functions[index + 1][1] = x;", + " }", + " insertFunction(t, func) {", + " const index = this.findFunction(t);", + " this.functions.splice(index + 1, 0, [func, t]);", + " }", + " removeFunction(index) {", + " return this.functions.splice(index, 1)[0][0];", + " }", + " getFunction(index) {", + " return this.functions[index][0];", + " }", + " setFunction(index, func) {", + " this.functions[index][0] = func;", + " }", + " get numOfFunctions() {", + " return this.functions.length;", + " }", + "}", + "", + "class PiecewiseBezier extends PiecewiseFunction {", + " constructor(curves = [[new Bezier(0, 1.0 / 3, 1.0 / 3 * 2, 1), 0]]) {", + " super();", + " this.type = \"function\";", + " this.functions = curves;", + " }", + " genValue(t = 0) {", + " const index = this.findFunction(t);", + " if (index === -1) {", + " return 0;", + " }", + " return this.functions[index][0].genValue((t - this.getStartX(index)) / (this.getEndX(index) - this.getStartX(index)));", + " }", + " toSVG(length, segments) {", + " if (segments < 1)", + " return \"\";", + " let result = [\"M\", 0, this.functions[0][0].p[0]].join(\" \");", + " for (let i = 1.0 / segments; i <= 1; i += 1.0 / segments) {", + " result = [result, \"L\", i * length, this.genValue(i)].join(\" \");", + " }", + " return result;", + " }", + " toJSON() {", + " return {", + " type: \"PiecewiseBezier\",", + " functions: this.functions.map(([bezier, start]) => ({ function: bezier.toJSON(), start: start })),", + " };", + " }", + " static fromJSON(json) {", + " return new PiecewiseBezier(json.functions.map((piecewiseFunction) => ([Bezier.fromJSON(piecewiseFunction.function), piecewiseFunction.start])));", + " }", + " clone() {", + " return new PiecewiseBezier(this.functions.map(([bezier, start]) => ([bezier.clone(), start])));", + " }", + "}", + "", + "function ValueGeneratorFromJSON(json) {", + " switch (json.type) {", + " case 'ConstantValue':", + " return ConstantValue.fromJSON(json);", + " case 'IntervalValue':", + " return IntervalValue.fromJSON(json);", + " case 'PiecewiseBezier':", + " return PiecewiseBezier.fromJSON(json);", + " default:", + " return new ConstantValue(0);", + " }", + "}", + "", + "class RandomQuatGenerator {", + " constructor() {", + " this.type = \"rotation\";", + " }", + " genValue(quat, t) {", + " let x, y, z, u, v, w;", + " do {", + " x = Math.random() * 2 - 1;", + " y = Math.random() * 2 - 1;", + " z = x * x + y * y;", + " } while (z > 1);", + " do {", + " u = Math.random() * 2 - 1;", + " v = Math.random() * 2 - 1;", + " w = u * u + v * v;", + " } while (w > 1);", + " const s = Math.sqrt((1 - z) / w);", + " quat.set(x, y, s * u, s * v);", + " return quat;", + " }", + " toJSON() {", + " return {", + " type: \"RandomQuat\"", + " };", + " }", + " static fromJSON(json) {", + " return new RandomQuatGenerator();", + " }", + " clone() {", + " return new RandomQuatGenerator();", + " }", + "}", + "", + "class AxisAngleGenerator {", + " constructor(axis, angle) {", + " this.axis = axis;", + " this.angle = angle;", + " this.type = 'rotation';", + " }", + " genValue(quat, t) {", + " return quat.setFromAxisAngle(this.axis, this.angle.genValue(t));", + " }", + " toJSON() {", + " return {", + " type: 'AxisAngle',", + " axis: { x: this.axis.x, y: this.axis.y, z: this.axis.z },", + " angle: this.angle.toJSON(),", + " };", + " }", + " static fromJSON(json) {", + " return new AxisAngleGenerator(new THREE.Vector3(json.axis.x, json.axis.y, json.axis.z), ValueGeneratorFromJSON(json.angle));", + " }", + " clone() {", + " return new AxisAngleGenerator(this.axis.clone(), this.angle.clone());", + " }", + "}", + "", + "class EulerGenerator {", + " constructor(angleX, angleY, angleZ, eulerOrder) {", + " this.angleX = angleX;", + " this.angleY = angleY;", + " this.angleZ = angleZ;", + " this.type = 'rotation';", + " this.eular = new THREE.Euler(0, 0, 0, eulerOrder);", + " }", + " genValue(quat, t) {", + " this.eular.set(this.angleX.genValue(t), this.angleY.genValue(t), this.angleZ.genValue(t));", + " return quat.setFromEuler(this.eular);", + " }", + " toJSON() {", + " return {", + " type: 'Euler',", + " angleX: this.angleX.toJSON(),", + " angleY: this.angleY.toJSON(),", + " angleZ: this.angleZ.toJSON(),", + " eulerOrder: this.eular.order,", + " };", + " }", + " static fromJSON(json) {", + " return new EulerGenerator(ValueGeneratorFromJSON(json.angleX), ValueGeneratorFromJSON(json.angleY), ValueGeneratorFromJSON(json.angleZ), json.eulerOrder);", + " }", + " clone() {", + " return new EulerGenerator(this.angleX, this.angleY, this.angleZ, this.eular.order);", + " }", + "}", + "", + "function RotationGeneratorFromJSON(json) {", + " switch (json.type) {", + " case 'AxisAngle':", + " return AxisAngleGenerator.fromJSON(json);", + " case 'Euler':", + " return EulerGenerator.fromJSON(json);", + " case 'RandomQuat':", + " return RandomQuatGenerator.fromJSON(json);", + " default:", + " return new RandomQuatGenerator();", + " }", + "}", + "", + "function GeneratorFromJSON(json) {", + " switch (json.type) {", + " case 'ConstantValue':", + " case 'IntervalValue':", + " case 'PiecewiseBezier':", + " return ValueGeneratorFromJSON(json);", + " case 'AxisAngle':", + " case 'RandomQuat':", + " case 'Euler':", + " return RotationGeneratorFromJSON(json);", + " default:", + " return new ConstantValue(0);", + " }", + "}", + "", + "class ColorOverLife {", + " constructor(color) {", + " this.color = color;", + " this.type = 'ColorOverLife';", + " }", + " initialize(particle) {", + " if (this.color.type === 'memorizedFunction') {", + " particle.colorOverLifeMemory = {};", + " this.color.startGen(particle.colorOverLifeMemory);", + " }", + " }", + " update(particle, delta) {", + " if (this.color.type === 'memorizedFunction') {", + " this.color.genColor(particle.color, particle.age / particle.life, particle.colorOverLifeMemory);", + " }", + " else {", + " this.color.genColor(particle.color, particle.age / particle.life);", + " }", + " particle.color.x *= particle.startColor.x;", + " particle.color.y *= particle.startColor.y;", + " particle.color.z *= particle.startColor.z;", + " particle.color.w *= particle.startColor.w;", + " }", + " frameUpdate(delta) { }", + " toJSON() {", + " return {", + " type: this.type,", + " color: this.color.toJSON(),", + " };", + " }", + " static fromJSON(json) {", + " return new ColorOverLife(ColorGeneratorFromJSON(json.color));", + " }", + " clone() {", + " return new ColorOverLife(this.color.clone());", + " }", + " reset() { }", + "}", + "", + "class RotationOverLife {", + " constructor(angularVelocity) {", + " this.angularVelocity = angularVelocity;", + " this.type = 'RotationOverLife';", + " this.dynamic = !(angularVelocity instanceof ConstantValue || angularVelocity instanceof IntervalValue);", + " }", + " initialize(particle) {", + " this.dynamic = !(this.angularVelocity instanceof ConstantValue || this.angularVelocity instanceof IntervalValue);", + " if (!this.dynamic && typeof particle.rotation === 'number') {", + " particle.angularVelocity = this.angularVelocity.genValue();", + " }", + " }", + " update(particle, delta) {", + " if (!this.dynamic) {", + " if (typeof particle.rotation === 'number') {", + " particle.rotation += delta * particle.angularVelocity;", + " }", + " }", + " else {", + " if (typeof particle.rotation === 'number') {", + " particle.rotation +=", + " delta * this.angularVelocity.genValue(particle.age / particle.life);", + " }", + " }", + " }", + " toJSON() {", + " return {", + " type: this.type,", + " angularVelocity: this.angularVelocity.toJSON(),", + " };", + " }", + " static fromJSON(json) {", + " return new RotationOverLife(ValueGeneratorFromJSON(json.angularVelocity));", + " }", + " frameUpdate(delta) { }", + " clone() {", + " return new RotationOverLife(this.angularVelocity.clone());", + " }", + " reset() { }", + "}", + "", + "const IdentityQuaternion = new THREE.Quaternion();", + "class Rotation3DOverLife {", + " constructor(angularVelocity) {", + " this.angularVelocity = angularVelocity;", + " this.type = 'Rotation3DOverLife';", + " this.tempQuat = new THREE.Quaternion();", + " this.dynamic = !(angularVelocity instanceof RandomQuatGenerator);", + " }", + " initialize(particle) {", + " this.dynamic = !(this.angularVelocity instanceof RandomQuatGenerator);", + " if (particle.rotation instanceof THREE.Quaternion) {", + " particle.angularVelocity = new THREE.Quaternion();", + " this.angularVelocity.genValue(particle.angularVelocity);", + " }", + " }", + " update(particle, delta) {", + " if (particle.rotation instanceof THREE.Quaternion) {", + " if (!this.dynamic) {", + " this.tempQuat.slerpQuaternions(IdentityQuaternion, particle.angularVelocity, delta);", + " particle.rotation.multiply(this.tempQuat);", + " }", + " else {", + " this.angularVelocity.genValue(this.tempQuat, particle.age / particle.life);", + " this.tempQuat.slerpQuaternions(IdentityQuaternion, this.tempQuat, delta);", + " particle.rotation.multiply(this.tempQuat);", + " }", + " }", + " }", + " toJSON() {", + " return {", + " type: this.type,", + " angularVelocity: this.angularVelocity.toJSON(),", + " };", + " }", + " static fromJSON(json) {", + " return new Rotation3DOverLife(RotationGeneratorFromJSON(json.angularVelocity));", + " }", + " frameUpdate(delta) { }", + " clone() {", + " return new Rotation3DOverLife(this.angularVelocity.clone());", + " }", + " reset() { }", + "}", + "", + "class ForceOverLife {", + " initialize(particle) { }", + " constructor(x, y, z) {", + " this.x = x;", + " this.y = y;", + " this.z = z;", + " this.type = 'ForceOverLife';", + " this._temp = new THREE.Vector3();", + " }", + " update(particle, delta) {", + " this._temp.set(this.x.genValue(particle.age / particle.life), this.y.genValue(particle.age / particle.life), this.z.genValue(particle.age / particle.life));", + " particle.velocity.addScaledVector(this._temp, delta);", + " }", + " toJSON() {", + " return {", + " type: this.type,", + " x: this.x.toJSON(),", + " y: this.y.toJSON(),", + " z: this.z.toJSON(),", + " };", + " }", + " static fromJSON(json) {", + " return new ForceOverLife(ValueGeneratorFromJSON(json.x), ValueGeneratorFromJSON(json.y), ValueGeneratorFromJSON(json.z));", + " }", + " frameUpdate(delta) { }", + " clone() {", + " return new ForceOverLife(this.x.clone(), this.y.clone(), this.z.clone());", + " }", + " reset() { }", + "}", + "", + "class SizeOverLife {", + " initialize(particle) {", + " }", + " constructor(size) {", + " this.size = size;", + " this.type = 'SizeOverLife';", + " }", + " update(particle) {", + " particle.size = particle.startSize * this.size.genValue(particle.age / particle.life);", + " }", + " toJSON() {", + " return {", + " type: this.type,", + " size: this.size.toJSON(),", + " };", + " }", + " static fromJSON(json) {", + " return new SizeOverLife(ValueGeneratorFromJSON(json.size));", + " }", + " frameUpdate(delta) {", + " }", + " clone() {", + " return new SizeOverLife(this.size.clone());", + " }", + " reset() {", + " }", + "}", + "", + "class SpeedOverLife {", + " initialize(particle) {", + " }", + " constructor(speed) {", + " this.speed = speed;", + " this.type = 'SpeedOverLife';", + " }", + " update(particle) {", + " particle.speedModifier = this.speed.genValue(particle.age / particle.life);", + " }", + " toJSON() {", + " return {", + " type: this.type,", + " speed: this.speed.toJSON(),", + " };", + " }", + " static fromJSON(json) {", + " return new SpeedOverLife(ValueGeneratorFromJSON(json.speed));", + " }", + " frameUpdate(delta) {", + " }", + " clone() {", + " return new SpeedOverLife(this.speed.clone());", + " }", + " reset() {", + " }", + "}", + "", + "class FrameOverLife {", + " constructor(frame) {", + " this.frame = frame;", + " this.type = 'FrameOverLife';", + " }", + " initialize(particle) {", + " if (!(this.frame instanceof PiecewiseBezier)) {", + " particle.uvTile = Math.floor(this.frame.genValue(0) + 0.001);", + " }", + " }", + " update(particle, delta) {", + " if (this.frame instanceof PiecewiseBezier) {", + " particle.uvTile = Math.floor(this.frame.genValue(particle.age / particle.life) + 0.001);", + " }", + " }", + " frameUpdate(delta) { }", + " toJSON() {", + " return {", + " type: this.type,", + " frame: this.frame.toJSON(),", + " };", + " }", + " static fromJSON(json) {", + " return new FrameOverLife(ValueGeneratorFromJSON(json.frame));", + " }", + " clone() {", + " return new FrameOverLife(this.frame.clone());", + " }", + " reset() { }", + "}", + "", + "new THREE.Vector3(0, 0, 1);", + "class OrbitOverLife {", + " constructor(orbitSpeed, axis = new THREE.Vector3(0, 1, 0)) {", + " this.orbitSpeed = orbitSpeed;", + " this.axis = axis;", + " this.type = 'OrbitOverLife';", + " this.temp = new THREE.Vector3();", + " this.rotation = new THREE.Quaternion();", + " this.line = new THREE.Line3();", + " }", + " initialize(particle) {", + " }", + " update(particle, delta) {", + " this.line.set(new THREE.Vector3(0, 0, 0), this.axis);", + " this.line.closestPointToPoint(particle.position, false, this.temp);", + " this.rotation.setFromAxisAngle(this.axis, this.orbitSpeed.genValue(particle.age / particle.life) * delta);", + " particle.position.sub(this.temp);", + " particle.position.applyQuaternion(this.rotation);", + " particle.position.add(this.temp);", + " }", + " frameUpdate(delta) {", + " }", + " toJSON() {", + " return {", + " type: this.type,", + " orbitSpeed: this.orbitSpeed.toJSON(),", + " axis: [this.axis.x, this.axis.y, this.axis.z],", + " };", + " }", + " static fromJSON(json) {", + " return new OrbitOverLife(ValueGeneratorFromJSON(json.orbitSpeed), json.axis ? new THREE.Vector3(json.axis[0], json.axis[1], json.axis[2]) : undefined);", + " }", + " clone() {", + " return new OrbitOverLife(this.orbitSpeed.clone());", + " }", + " reset() {", + " }", + "}", + "", + "class WidthOverLength {", + " initialize(particle) {", + " }", + " constructor(width) {", + " this.width = width;", + " this.type = 'WidthOverLength';", + " }", + " update(particle) {", + " if (particle instanceof TrailParticle) {", + " const iter = particle.previous.values();", + " for (let i = 0; i < particle.previous.length; i++) {", + " const cur = iter.next();", + " // We added a multiplication with size because it's more convenient for the extension.", + " cur.value.size = this.width.genValue((particle.previous.length - i) / particle.length) * particle.size;", + " }", + " }", + " }", + " frameUpdate(delta) {", + " }", + " toJSON() {", + " return {", + " type: this.type,", + " width: this.width.toJSON(),", + " };", + " }", + " static fromJSON(json) {", + " return new WidthOverLength(ValueGeneratorFromJSON(json.width));", + " }", + " clone() {", + " return new WidthOverLength(this.width.clone());", + " }", + " reset() {", + " }", + "}", + "", + "class ApplyForce {", + " constructor(direction, magnitude) {", + " this.direction = direction;", + " this.magnitude = magnitude;", + " this.type = 'ApplyForce';", + " this.magnitudeValue = this.magnitude.genValue();", + " }", + " initialize(particle) {", + " }", + " update(particle, delta) {", + " particle.velocity.addScaledVector(this.direction, this.magnitudeValue * delta);", + " }", + " frameUpdate(delta) {", + " this.magnitudeValue = this.magnitude.genValue();", + " }", + " toJSON() {", + " return {", + " type: this.type,", + " direction: [this.direction.x, this.direction.y, this.direction.z],", + " magnitude: this.magnitude.toJSON(),", + " };", + " }", + " static fromJSON(json) {", + " var _a;", + " return new ApplyForce(new THREE.Vector3(json.direction[0], json.direction[1], json.direction[2]), ValueGeneratorFromJSON((_a = json.magnitude) !== null && _a !== void 0 ? _a : json.force));", + " }", + " clone() {", + " return new ApplyForce(this.direction.clone(), this.magnitude.clone());", + " }", + " reset() {", + " }", + "}", + "", + "class GravityForce {", + " constructor(center, magnitude) {", + " this.center = center;", + " this.magnitude = magnitude;", + " this.type = 'GravityForce';", + " this.temp = new THREE.Vector3();", + " }", + " initialize(particle) {", + " }", + " update(particle, delta) {", + " this.temp.copy(this.center).sub(particle.position).normalize();", + " particle.velocity.addScaledVector(this.temp, this.magnitude / particle.position.distanceToSquared(this.center) * delta);", + " }", + " frameUpdate(delta) {", + " }", + " toJSON() {", + " return {", + " type: this.type,", + " center: [this.center.x, this.center.y, this.center.z],", + " magnitude: this.magnitude,", + " };", + " }", + " static fromJSON(json) {", + " return new GravityForce(new THREE.Vector3(json.center[0], json.center[1], json.center[2]), json.magnitude);", + " }", + " clone() {", + " return new GravityForce(this.center.clone(), this.magnitude);", + " }", + " reset() {", + " }", + "}", + "", + "new THREE.Vector3(0, 0, 1);", + "class ChangeEmitDirection {", + " constructor(angle) {", + " this.angle = angle;", + " this.type = 'ChangeEmitDirection';", + " this._temp = new THREE.Vector3();", + " this._q = new THREE.Quaternion();", + " }", + " initialize(particle) {", + " const len = particle.velocity.length();", + " if (len == 0)", + " return;", + " particle.velocity.normalize();", + " if (particle.velocity.x === 0 && particle.velocity.y === 0) {", + " this._temp.set(0, particle.velocity.z, 0);", + " }", + " else {", + " this._temp.set(-particle.velocity.y, particle.velocity.x, 0);", + " }", + " this._q.setFromAxisAngle(this._temp.normalize(), this.angle.genValue());", + " this._temp.copy(particle.velocity);", + " particle.velocity.applyQuaternion(this._q);", + " this._q.setFromAxisAngle(this._temp, Math.random() * Math.PI * 2);", + " particle.velocity.applyQuaternion(this._q);", + " particle.velocity.setLength(len);", + " }", + " update(particle, delta) {", + " }", + " frameUpdate(delta) {", + " }", + " toJSON() {", + " return {", + " type: this.type,", + " angle: this.angle.toJSON(),", + " };", + " }", + " static fromJSON(json) {", + " return new ChangeEmitDirection(ValueGeneratorFromJSON(json.angle));", + " }", + " clone() {", + " return new ChangeEmitDirection(this.angle);", + " }", + " reset() {", + " }", + "}", + "", + "const VECTOR_ONE = new THREE.Vector3(1, 1, 1);", + "const VECTOR_Z = new THREE.Vector3(0, 0, 1);", + "let SubParticleEmitMode = void 0;", + "(function (SubParticleEmitMode) {", + " SubParticleEmitMode[SubParticleEmitMode[\"Death\"] = 0] = \"Death\";", + " SubParticleEmitMode[SubParticleEmitMode[\"Birth\"] = 1] = \"Birth\";", + " SubParticleEmitMode[SubParticleEmitMode[\"Frame\"] = 2] = \"Frame\";", + "})(SubParticleEmitMode || (SubParticleEmitMode = {}));", + "class EmitSubParticleSystem {", + " constructor(particleSystem, useVelocityAsBasis, subParticleSystem, mode = SubParticleEmitMode.Frame, emitProbability = 1) {", + " this.particleSystem = particleSystem;", + " this.useVelocityAsBasis = useVelocityAsBasis;", + " this.subParticleSystem = subParticleSystem;", + " this.mode = mode;", + " this.emitProbability = emitProbability;", + " this.type = 'EmitSubParticleSystem';", + " this.q_ = new THREE.Quaternion();", + " this.v_ = new THREE.Vector3();", + " this.v2_ = new THREE.Vector3();", + " this.subEmissions = new Array();", + " if (this.subParticleSystem && this.subParticleSystem.system) {", + " this.subParticleSystem.system.onlyUsedByOther = true;", + " }", + " }", + " initialize(particle) {", + " }", + " update(particle, delta) {", + " if (this.mode === SubParticleEmitMode.Frame) {", + " this.emit(particle, delta);", + " }", + " else if (this.mode === SubParticleEmitMode.Birth && particle.age === 0) {", + " this.emit(particle, delta);", + " }", + " else if (this.mode === SubParticleEmitMode.Death && particle.age + delta >= particle.life) {", + " this.emit(particle, delta);", + " }", + " }", + " emit(particle, delta) {", + " if (!this.subParticleSystem)", + " return;", + " if (Math.random() > this.emitProbability) {", + " return;", + " }", + " const m = new THREE.Matrix4();", + " this.setMatrixFromParticle(m, particle);", + " this.subEmissions.push({", + " burstIndex: 0,", + " burstWaveIndex: 0,", + " time: 0,", + " waitEmiting: 0,", + " matrix: m,", + " travelDistance: 0,", + " particle: particle,", + " });", + " }", + " frameUpdate(delta) {", + " if (!this.subParticleSystem)", + " return;", + " for (let i = 0; i < this.subEmissions.length; i++) {", + " if (this.subEmissions[i].time >= this.subParticleSystem.system.duration) {", + " this.subEmissions[i] = this.subEmissions[this.subEmissions.length - 1];", + " this.subEmissions.length = this.subEmissions.length - 1;", + " i--;", + " }", + " else {", + " let subEmissionState = this.subEmissions[i];", + " if (subEmissionState.particle && subEmissionState.particle.age < subEmissionState.particle.life) {", + " this.setMatrixFromParticle(subEmissionState.matrix, subEmissionState.particle);", + " }", + " else {", + " subEmissionState.particle = undefined;", + " }", + " this.subParticleSystem.system.emit(delta, subEmissionState, subEmissionState.matrix);", + " }", + " }", + " }", + " toJSON() {", + " return {", + " type: this.type,", + " subParticleSystem: this.subParticleSystem ? this.subParticleSystem.uuid : '',", + " useVelocityAsBasis: this.useVelocityAsBasis,", + " mode: this.mode,", + " emitProbability: this.emitProbability,", + " };", + " }", + " static fromJSON(json, particleSystem) {", + " return new EmitSubParticleSystem(particleSystem, json.useVelocityAsBasis, json.subParticleSystem, json.mode, json.emitProbability);", + " }", + " clone() {", + " return new EmitSubParticleSystem(this.particleSystem, this.useVelocityAsBasis, this.subParticleSystem, this.mode, this.emitProbability);", + " }", + " reset() { }", + " setMatrixFromParticle(m, particle) {", + " let rotation;", + " if (particle.rotation === undefined || this.useVelocityAsBasis) {", + " if (particle.velocity.x === 0 &&", + " particle.velocity.y === 0 &&", + " (particle.velocity.z === 1 || particle.velocity.z === 0)) {", + " m.set(1, 0, 0, particle.position.x, 0, 1, 0, particle.position.y, 0, 0, 1, particle.position.z, 0, 0, 0, 1);", + " }", + " else {", + " this.v_.copy(VECTOR_Z).cross(particle.velocity);", + " this.v2_.copy(particle.velocity).cross(this.v_);", + " const len = this.v_.length();", + " const len2 = this.v2_.length();", + " m.set(this.v_.x / len, this.v2_.x / len2, particle.velocity.x, particle.position.x, this.v_.y / len, this.v2_.y / len2, particle.velocity.y, particle.position.y, this.v_.z / len, this.v2_.z / len2, particle.velocity.z, particle.position.z, 0, 0, 0, 1);", + " }", + " }", + " else {", + " if (particle.rotation instanceof THREE.Quaternion) {", + " rotation = particle.rotation;", + " }", + " else {", + " this.q_.setFromAxisAngle(VECTOR_Z, particle.rotation);", + " rotation = this.q_;", + " }", + " m.compose(particle.position, rotation, VECTOR_ONE);", + " }", + " if (!this.particleSystem.worldSpace) {", + " m.multiplyMatrices(this.particleSystem.emitter.matrixWorld, m);", + " }", + " }", + "}", + "", + "const F2 = 0.5 * (Math.sqrt(3.0) - 1.0);", + "const G2 = (3.0 - Math.sqrt(3.0)) / 6.0;", + "const F3 = 1.0 / 3.0;", + "const G3 = 1.0 / 6.0;", + "const F4 = (Math.sqrt(5.0) - 1.0) / 4.0;", + "const G4 = (5.0 - Math.sqrt(5.0)) / 20.0;", + "const grad3 = new Float32Array([1, 1, 0,", + " -1, 1, 0,", + " 1, -1, 0,", + " -1, -1, 0,", + " 1, 0, 1,", + " -1, 0, 1,", + " 1, 0, -1,", + " -1, 0, -1,", + " 0, 1, 1,", + " 0, -1, 1,", + " 0, 1, -1,", + " 0, -1, -1]);", + "const grad4 = new Float32Array([0, 1, 1, 1, 0, 1, 1, -1, 0, 1, -1, 1, 0, 1, -1, -1,", + " 0, -1, 1, 1, 0, -1, 1, -1, 0, -1, -1, 1, 0, -1, -1, -1,", + " 1, 0, 1, 1, 1, 0, 1, -1, 1, 0, -1, 1, 1, 0, -1, -1,", + " -1, 0, 1, 1, -1, 0, 1, -1, -1, 0, -1, 1, -1, 0, -1, -1,", + " 1, 1, 0, 1, 1, 1, 0, -1, 1, -1, 0, 1, 1, -1, 0, -1,", + " -1, 1, 0, 1, -1, 1, 0, -1, -1, -1, 0, 1, -1, -1, 0, -1,", + " 1, 1, 1, 0, 1, 1, -1, 0, 1, -1, 1, 0, 1, -1, -1, 0,", + " -1, 1, 1, 0, -1, 1, -1, 0, -1, -1, 1, 0, -1, -1, -1, 0]);", + "class SimplexNoise {", + " constructor(randomOrSeed = Math.random) {", + " const random = typeof randomOrSeed == 'function' ? randomOrSeed : alea(randomOrSeed);", + " this.p = buildPermutationTable(random);", + " this.perm = new Uint8Array(512);", + " this.permMod12 = new Uint8Array(512);", + " for (let i = 0; i < 512; i++) {", + " this.perm[i] = this.p[i & 255];", + " this.permMod12[i] = this.perm[i] % 12;", + " }", + " }", + " noise2D(x, y) {", + " const permMod12 = this.permMod12;", + " const perm = this.perm;", + " let n0 = 0;", + " let n1 = 0;", + " let n2 = 0;", + " const s = (x + y) * F2;", + " const i = Math.floor(x + s);", + " const j = Math.floor(y + s);", + " const t = (i + j) * G2;", + " const X0 = i - t;", + " const Y0 = j - t;", + " const x0 = x - X0;", + " const y0 = y - Y0;", + " let i1, j1;", + " if (x0 > y0) {", + " i1 = 1;", + " j1 = 0;", + " }", + " else {", + " i1 = 0;", + " j1 = 1;", + " }", + " const x1 = x0 - i1 + G2;", + " const y1 = y0 - j1 + G2;", + " const x2 = x0 - 1.0 + 2.0 * G2;", + " const y2 = y0 - 1.0 + 2.0 * G2;", + " const ii = i & 255;", + " const jj = j & 255;", + " let t0 = 0.5 - x0 * x0 - y0 * y0;", + " if (t0 >= 0) {", + " const gi0 = permMod12[ii + perm[jj]] * 3;", + " t0 *= t0;", + " n0 = t0 * t0 * (grad3[gi0] * x0 + grad3[gi0 + 1] * y0);", + " }", + " let t1 = 0.5 - x1 * x1 - y1 * y1;", + " if (t1 >= 0) {", + " const gi1 = permMod12[ii + i1 + perm[jj + j1]] * 3;", + " t1 *= t1;", + " n1 = t1 * t1 * (grad3[gi1] * x1 + grad3[gi1 + 1] * y1);", + " }", + " let t2 = 0.5 - x2 * x2 - y2 * y2;", + " if (t2 >= 0) {", + " const gi2 = permMod12[ii + 1 + perm[jj + 1]] * 3;", + " t2 *= t2;", + " n2 = t2 * t2 * (grad3[gi2] * x2 + grad3[gi2 + 1] * y2);", + " }", + " return 70.0 * (n0 + n1 + n2);", + " }", + " noise3D(x, y, z) {", + " const permMod12 = this.permMod12;", + " const perm = this.perm;", + " let n0, n1, n2, n3;", + " const s = (x + y + z) * F3;", + " const i = Math.floor(x + s);", + " const j = Math.floor(y + s);", + " const k = Math.floor(z + s);", + " const t = (i + j + k) * G3;", + " const X0 = i - t;", + " const Y0 = j - t;", + " const Z0 = k - t;", + " const x0 = x - X0;", + " const y0 = y - Y0;", + " const z0 = z - Z0;", + " let i1, j1, k1;", + " let i2, j2, k2;", + " if (x0 >= y0) {", + " if (y0 >= z0) {", + " i1 = 1;", + " j1 = 0;", + " k1 = 0;", + " i2 = 1;", + " j2 = 1;", + " k2 = 0;", + " }", + " else if (x0 >= z0) {", + " i1 = 1;", + " j1 = 0;", + " k1 = 0;", + " i2 = 1;", + " j2 = 0;", + " k2 = 1;", + " }", + " else {", + " i1 = 0;", + " j1 = 0;", + " k1 = 1;", + " i2 = 1;", + " j2 = 0;", + " k2 = 1;", + " }", + " }", + " else {", + " if (y0 < z0) {", + " i1 = 0;", + " j1 = 0;", + " k1 = 1;", + " i2 = 0;", + " j2 = 1;", + " k2 = 1;", + " }", + " else if (x0 < z0) {", + " i1 = 0;", + " j1 = 1;", + " k1 = 0;", + " i2 = 0;", + " j2 = 1;", + " k2 = 1;", + " }", + " else {", + " i1 = 0;", + " j1 = 1;", + " k1 = 0;", + " i2 = 1;", + " j2 = 1;", + " k2 = 0;", + " }", + " }", + " const x1 = x0 - i1 + G3;", + " const y1 = y0 - j1 + G3;", + " const z1 = z0 - k1 + G3;", + " const x2 = x0 - i2 + 2.0 * G3;", + " const y2 = y0 - j2 + 2.0 * G3;", + " const z2 = z0 - k2 + 2.0 * G3;", + " const x3 = x0 - 1.0 + 3.0 * G3;", + " const y3 = y0 - 1.0 + 3.0 * G3;", + " const z3 = z0 - 1.0 + 3.0 * G3;", + " const ii = i & 255;", + " const jj = j & 255;", + " const kk = k & 255;", + " let t0 = 0.6 - x0 * x0 - y0 * y0 - z0 * z0;", + " if (t0 < 0)", + " n0 = 0.0;", + " else {", + " const gi0 = permMod12[ii + perm[jj + perm[kk]]] * 3;", + " t0 *= t0;", + " n0 = t0 * t0 * (grad3[gi0] * x0 + grad3[gi0 + 1] * y0 + grad3[gi0 + 2] * z0);", + " }", + " let t1 = 0.6 - x1 * x1 - y1 * y1 - z1 * z1;", + " if (t1 < 0)", + " n1 = 0.0;", + " else {", + " const gi1 = permMod12[ii + i1 + perm[jj + j1 + perm[kk + k1]]] * 3;", + " t1 *= t1;", + " n1 = t1 * t1 * (grad3[gi1] * x1 + grad3[gi1 + 1] * y1 + grad3[gi1 + 2] * z1);", + " }", + " let t2 = 0.6 - x2 * x2 - y2 * y2 - z2 * z2;", + " if (t2 < 0)", + " n2 = 0.0;", + " else {", + " const gi2 = permMod12[ii + i2 + perm[jj + j2 + perm[kk + k2]]] * 3;", + " t2 *= t2;", + " n2 = t2 * t2 * (grad3[gi2] * x2 + grad3[gi2 + 1] * y2 + grad3[gi2 + 2] * z2);", + " }", + " let t3 = 0.6 - x3 * x3 - y3 * y3 - z3 * z3;", + " if (t3 < 0)", + " n3 = 0.0;", + " else {", + " const gi3 = permMod12[ii + 1 + perm[jj + 1 + perm[kk + 1]]] * 3;", + " t3 *= t3;", + " n3 = t3 * t3 * (grad3[gi3] * x3 + grad3[gi3 + 1] * y3 + grad3[gi3 + 2] * z3);", + " }", + " return 32.0 * (n0 + n1 + n2 + n3);", + " }", + " noise4D(x, y, z, w) {", + " const perm = this.perm;", + " let n0, n1, n2, n3, n4;", + " const s = (x + y + z + w) * F4;", + " const i = Math.floor(x + s);", + " const j = Math.floor(y + s);", + " const k = Math.floor(z + s);", + " const l = Math.floor(w + s);", + " const t = (i + j + k + l) * G4;", + " const X0 = i - t;", + " const Y0 = j - t;", + " const Z0 = k - t;", + " const W0 = l - t;", + " const x0 = x - X0;", + " const y0 = y - Y0;", + " const z0 = z - Z0;", + " const w0 = w - W0;", + " let rankx = 0;", + " let ranky = 0;", + " let rankz = 0;", + " let rankw = 0;", + " if (x0 > y0)", + " rankx++;", + " else", + " ranky++;", + " if (x0 > z0)", + " rankx++;", + " else", + " rankz++;", + " if (x0 > w0)", + " rankx++;", + " else", + " rankw++;", + " if (y0 > z0)", + " ranky++;", + " else", + " rankz++;", + " if (y0 > w0)", + " ranky++;", + " else", + " rankw++;", + " if (z0 > w0)", + " rankz++;", + " else", + " rankw++;", + " const i1 = rankx >= 3 ? 1 : 0;", + " const j1 = ranky >= 3 ? 1 : 0;", + " const k1 = rankz >= 3 ? 1 : 0;", + " const l1 = rankw >= 3 ? 1 : 0;", + " const i2 = rankx >= 2 ? 1 : 0;", + " const j2 = ranky >= 2 ? 1 : 0;", + " const k2 = rankz >= 2 ? 1 : 0;", + " const l2 = rankw >= 2 ? 1 : 0;", + " const i3 = rankx >= 1 ? 1 : 0;", + " const j3 = ranky >= 1 ? 1 : 0;", + " const k3 = rankz >= 1 ? 1 : 0;", + " const l3 = rankw >= 1 ? 1 : 0;", + " const x1 = x0 - i1 + G4;", + " const y1 = y0 - j1 + G4;", + " const z1 = z0 - k1 + G4;", + " const w1 = w0 - l1 + G4;", + " const x2 = x0 - i2 + 2.0 * G4;", + " const y2 = y0 - j2 + 2.0 * G4;", + " const z2 = z0 - k2 + 2.0 * G4;", + " const w2 = w0 - l2 + 2.0 * G4;", + " const x3 = x0 - i3 + 3.0 * G4;", + " const y3 = y0 - j3 + 3.0 * G4;", + " const z3 = z0 - k3 + 3.0 * G4;", + " const w3 = w0 - l3 + 3.0 * G4;", + " const x4 = x0 - 1.0 + 4.0 * G4;", + " const y4 = y0 - 1.0 + 4.0 * G4;", + " const z4 = z0 - 1.0 + 4.0 * G4;", + " const w4 = w0 - 1.0 + 4.0 * G4;", + " const ii = i & 255;", + " const jj = j & 255;", + " const kk = k & 255;", + " const ll = l & 255;", + " let t0 = 0.6 - x0 * x0 - y0 * y0 - z0 * z0 - w0 * w0;", + " if (t0 < 0)", + " n0 = 0.0;", + " else {", + " const gi0 = (perm[ii + perm[jj + perm[kk + perm[ll]]]] % 32) * 4;", + " t0 *= t0;", + " n0 = t0 * t0 * (grad4[gi0] * x0 + grad4[gi0 + 1] * y0 + grad4[gi0 + 2] * z0 + grad4[gi0 + 3] * w0);", + " }", + " let t1 = 0.6 - x1 * x1 - y1 * y1 - z1 * z1 - w1 * w1;", + " if (t1 < 0)", + " n1 = 0.0;", + " else {", + " const gi1 = (perm[ii + i1 + perm[jj + j1 + perm[kk + k1 + perm[ll + l1]]]] % 32) * 4;", + " t1 *= t1;", + " n1 = t1 * t1 * (grad4[gi1] * x1 + grad4[gi1 + 1] * y1 + grad4[gi1 + 2] * z1 + grad4[gi1 + 3] * w1);", + " }", + " let t2 = 0.6 - x2 * x2 - y2 * y2 - z2 * z2 - w2 * w2;", + " if (t2 < 0)", + " n2 = 0.0;", + " else {", + " const gi2 = (perm[ii + i2 + perm[jj + j2 + perm[kk + k2 + perm[ll + l2]]]] % 32) * 4;", + " t2 *= t2;", + " n2 = t2 * t2 * (grad4[gi2] * x2 + grad4[gi2 + 1] * y2 + grad4[gi2 + 2] * z2 + grad4[gi2 + 3] * w2);", + " }", + " let t3 = 0.6 - x3 * x3 - y3 * y3 - z3 * z3 - w3 * w3;", + " if (t3 < 0)", + " n3 = 0.0;", + " else {", + " const gi3 = (perm[ii + i3 + perm[jj + j3 + perm[kk + k3 + perm[ll + l3]]]] % 32) * 4;", + " t3 *= t3;", + " n3 = t3 * t3 * (grad4[gi3] * x3 + grad4[gi3 + 1] * y3 + grad4[gi3 + 2] * z3 + grad4[gi3 + 3] * w3);", + " }", + " let t4 = 0.6 - x4 * x4 - y4 * y4 - z4 * z4 - w4 * w4;", + " if (t4 < 0)", + " n4 = 0.0;", + " else {", + " const gi4 = (perm[ii + 1 + perm[jj + 1 + perm[kk + 1 + perm[ll + 1]]]] % 32) * 4;", + " t4 *= t4;", + " n4 = t4 * t4 * (grad4[gi4] * x4 + grad4[gi4 + 1] * y4 + grad4[gi4 + 2] * z4 + grad4[gi4 + 3] * w4);", + " }", + " return 27.0 * (n0 + n1 + n2 + n3 + n4);", + " }", + "}", + "function buildPermutationTable(random) {", + " const p = new Uint8Array(256);", + " for (let i = 0; i < 256; i++) {", + " p[i] = i;", + " }", + " for (let i = 0; i < 255; i++) {", + " const r = i + ~~(random() * (256 - i));", + " const aux = p[i];", + " p[i] = p[r];", + " p[r] = aux;", + " }", + " return p;", + "}", + "function alea(seed) {", + " let s0 = 0;", + " let s1 = 0;", + " let s2 = 0;", + " let c = 1;", + " const mash = masher();", + " s0 = mash(' ');", + " s1 = mash(' ');", + " s2 = mash(' ');", + " s0 -= mash(seed);", + " if (s0 < 0) {", + " s0 += 1;", + " }", + " s1 -= mash(seed);", + " if (s1 < 0) {", + " s1 += 1;", + " }", + " s2 -= mash(seed);", + " if (s2 < 0) {", + " s2 += 1;", + " }", + " return function () {", + " const t = 2091639 * s0 + c * 2.3283064365386963e-10;", + " s0 = s1;", + " s1 = s2;", + " return s2 = t - (c = t | 0);", + " };", + "}", + "function masher() {", + " let n = 0xefc8249d;", + " return function (data) {", + " data = data.toString();", + " for (let i = 0; i < data.length; i++) {", + " n += data.charCodeAt(i);", + " let h = 0.02519603282416938 * n;", + " n = h >>> 0;", + " h -= n;", + " h *= n;", + " n = h >>> 0;", + " h -= n;", + " n += h * 0x100000000;", + " }", + " return (n >>> 0) * 2.3283064365386963e-10;", + " };", + "}", + "", + "class TurbulenceField {", + " constructor(scale, octaves, velocityMultiplier, timeScale) {", + " this.scale = scale;", + " this.octaves = octaves;", + " this.velocityMultiplier = velocityMultiplier;", + " this.timeScale = timeScale;", + " this.type = 'TurbulenceField';", + " this.generator = new SimplexNoise();", + " this.timeOffset = new THREE.Vector3();", + " this.temp = new THREE.Vector3();", + " this.temp2 = new THREE.Vector3();", + " this.timeOffset.x = Math.random() / this.scale.x * this.timeScale.x;", + " this.timeOffset.y = Math.random() / this.scale.y * this.timeScale.y;", + " this.timeOffset.z = Math.random() / this.scale.z * this.timeScale.z;", + " }", + " initialize(particle) {", + " }", + " update(particle, delta) {", + " const x = particle.position.x / this.scale.x;", + " const y = particle.position.y / this.scale.y;", + " const z = particle.position.z / this.scale.z;", + " this.temp.set(0, 0, 0);", + " let lvl = 1;", + " for (let i = 0; i < this.octaves; i++) {", + " this.temp2.set(this.generator.noise4D(x * lvl, y * lvl, z * lvl, this.timeOffset.x * lvl) / lvl, this.generator.noise4D(x * lvl, y * lvl, z * lvl, this.timeOffset.y * lvl) / lvl, this.generator.noise4D(x * lvl, y * lvl, z * lvl, this.timeOffset.z * lvl) / lvl);", + " this.temp.add(this.temp2);", + " lvl *= 2;", + " }", + " this.temp.multiply(this.velocityMultiplier);", + " particle.velocity.addScaledVector(this.temp, delta);", + " }", + " toJSON() {", + " return {", + " type: this.type,", + " scale: [this.scale.x, this.scale.y, this.scale.z],", + " octaves: this.octaves,", + " velocityMultiplier: [this.velocityMultiplier.x, this.velocityMultiplier.y, this.velocityMultiplier.z],", + " timeScale: [this.timeScale.x, this.timeScale.y, this.timeScale.z],", + " };", + " }", + " frameUpdate(delta) {", + " this.timeOffset.x += delta * this.timeScale.x;", + " this.timeOffset.y += delta * this.timeScale.y;", + " this.timeOffset.z += delta * this.timeScale.z;", + " }", + " static fromJSON(json) {", + " return new TurbulenceField(new THREE.Vector3(json.scale[0], json.scale[1], json.scale[2]), json.octaves, new THREE.Vector3(json.velocityMultiplier[0], json.velocityMultiplier[1], json.velocityMultiplier[2]), new THREE.Vector3(json.timeScale[0], json.timeScale[1], json.timeScale[2]));", + " }", + " clone() {", + " return new TurbulenceField(this.scale.clone(), this.octaves, this.velocityMultiplier.clone(), this.timeScale.clone());", + " }", + " reset() {", + " }", + "}", + "", + "function randomInt(a, b) {", + " return Math.floor(Math.random() * (b - a)) + a;", + "}", + "", + "const generators = [];", + "const tempV$2 = new THREE.Vector3();", + "const tempQ$2 = new THREE.Quaternion();", + "class Noise {", + " constructor(frequency, power, positionAmount = new ConstantValue(1), rotationAmount = new ConstantValue(0)) {", + " this.frequency = frequency;", + " this.power = power;", + " this.positionAmount = positionAmount;", + " this.rotationAmount = rotationAmount;", + " this.type = 'Noise';", + " this.duration = 0;", + " if (generators.length === 0) {", + " for (let i = 0; i < 100; i++) {", + " generators.push(new SimplexNoise());", + " }", + " }", + " }", + " initialize(particle) {", + " particle.lastPosNoise = new THREE.Vector3();", + " if (typeof particle.rotation === 'number') {", + " particle.lastRotNoise = 0;", + " }", + " else {", + " particle.lastRotNoise = new THREE.Quaternion();", + " }", + " particle.generatorIndex = [randomInt(0, 100), randomInt(0, 100), randomInt(0, 100), randomInt(0, 100)];", + " }", + " update(particle, _) {", + " let frequency = this.frequency.genValue(particle.age / particle.life);", + " let power = this.power.genValue(particle.age / particle.life);", + " let positionAmount = this.positionAmount.genValue(particle.age / particle.life);", + " let rotationAmount = this.rotationAmount.genValue(particle.age / particle.life);", + " if (positionAmount > 0 && particle.lastPosNoise !== undefined) {", + " particle.position.sub(particle.lastPosNoise);", + " tempV$2.set(generators[particle.generatorIndex[0]].noise2D(0, particle.age * frequency) *", + " power *", + " positionAmount, generators[particle.generatorIndex[1]].noise2D(0, particle.age * frequency) *", + " power *", + " positionAmount, generators[particle.generatorIndex[2]].noise2D(0, particle.age * frequency) *", + " power *", + " positionAmount);", + " particle.position.add(tempV$2);", + " particle.lastPosNoise.copy(tempV$2);", + " }", + " if (rotationAmount > 0 && particle.lastRotNoise !== undefined) {", + " if (typeof particle.rotation === 'number') {", + " particle.rotation -= particle.lastRotNoise;", + " particle.rotation +=", + " generators[particle.generatorIndex[3]].noise2D(0, particle.age * frequency) *", + " Math.PI *", + " power *", + " rotationAmount;", + " }", + " else {", + " particle.lastRotNoise.invert();", + " particle.rotation.multiply(particle.lastRotNoise);", + " tempQ$2", + " .set(generators[particle.generatorIndex[0]].noise2D(0, particle.age * frequency) *", + " power *", + " rotationAmount, generators[particle.generatorIndex[1]].noise2D(0, particle.age * frequency) *", + " power *", + " rotationAmount, generators[particle.generatorIndex[2]].noise2D(0, particle.age * frequency) *", + " power *", + " rotationAmount, generators[particle.generatorIndex[3]].noise2D(0, particle.age * frequency) *", + " power *", + " rotationAmount)", + " .normalize();", + " particle.rotation.multiply(tempQ$2);", + " particle.lastRotNoise.copy(tempQ$2);", + " }", + " }", + " }", + " toJSON() {", + " return {", + " type: this.type,", + " frequency: this.frequency.toJSON(),", + " power: this.power.toJSON(),", + " positionAmount: this.positionAmount.toJSON(),", + " rotationAmount: this.rotationAmount.toJSON(),", + " };", + " }", + " frameUpdate(delta) {", + " this.duration += delta;", + " }", + " static fromJSON(json) {", + " return new Noise(ValueGeneratorFromJSON(json.frequency), ValueGeneratorFromJSON(json.power), ValueGeneratorFromJSON(json.positionAmount), ValueGeneratorFromJSON(json.rotationAmount));", + " }", + " clone() {", + " return new Noise(this.frequency.clone(), this.power.clone(), this.positionAmount.clone(), this.rotationAmount.clone());", + " }", + " reset() { }", + "}", + "", + "class TextureSequencer {", + " constructor(scaleX = 0, scaleY = 0, position = new THREE.Vector3()) {", + " this.scaleX = scaleX;", + " this.scaleY = scaleY;", + " this.position = position;", + " this.locations = [];", + " }", + " transform(position, index) {", + " position.x = this.locations[index % this.locations.length].x * this.scaleX + this.position.x;", + " position.y = this.locations[index % this.locations.length].y * this.scaleY + this.position.y;", + " position.z = this.position.z;", + " }", + " static fromJSON(json) {", + " const textureSequencer = new TextureSequencer(json.scaleX, json.scaleY, new THREE.Vector3(json.position[0], json.position[1], json.position[2]));", + " textureSequencer.locations = json.locations.map((loc) => new THREE.Vector2(loc.x, loc.y));", + " return textureSequencer;", + " }", + " clone() {", + " const textureSequencer = new TextureSequencer(this.scaleX, this.scaleY, this.position.clone());", + " textureSequencer.locations = this.locations.map(loc => loc.clone());", + " return textureSequencer;", + " }", + " toJSON() {", + " return {", + " scaleX: this.scaleX,", + " scaleY: this.scaleY,", + " position: this.position,", + " locations: this.locations.map(loc => ({", + " x: loc.x,", + " y: loc.y,", + " }))", + " };", + " }", + " fromImage(img, threshold) {", + " const canvas = document.createElement(\"canvas\");", + " canvas.width = img.width;", + " canvas.height = img.height;", + " const ctx = canvas.getContext(\"2d\");", + " if (!ctx) {", + " return;", + " }", + " ctx.drawImage(img, 0, 0);", + " const data = ctx.getImageData(0, 0, canvas.width, canvas.height, { colorSpace: \"srgb\" });", + " canvas.remove();", + " this.locations.length = 0;", + " for (let i = 0; i < data.height; i++) {", + " for (let j = 0; j < data.width; j++) {", + " if (data.data[(i * data.width + j) * 4 + 3] > threshold) {", + " this.locations.push(new THREE.Vector2(j, data.height - i));", + " }", + " }", + " }", + " }", + "}", + "", + "function SequencerFromJSON(json) {", + " switch (json.type) {", + " case 'TextureSequencer':", + " return TextureSequencer.fromJSON(json);", + " default:", + " return new TextureSequencer();", + " }", + "}", + "", + "class ApplySequences {", + " constructor(delayBetweenParticles) {", + " this.type = 'ApplySequences';", + " this.sequencers = [];", + " this.time = 0;", + " this.index = 0;", + " this.pCount = 0;", + " this.tempV = new THREE.Vector3();", + " this.delay = delayBetweenParticles;", + " }", + " initialize(particle) {", + " particle.id = this.pCount;", + " particle.dst = new THREE.Vector3();", + " particle.begin = new THREE.Vector3();", + " particle.inMotion = false;", + " this.pCount++;", + " }", + " reset() {", + " this.time = 0;", + " this.index = 0;", + " this.pCount = 0;", + " }", + " update(particle, delta) {", + " const sequencer = this.sequencers[this.index];", + " const delay = particle.id * this.delay;", + " if (this.time >= sequencer[0].a + delay && this.time <= sequencer[0].b + delay) {", + " if (!particle.inMotion) {", + " particle.inMotion = true;", + " particle.begin.copy(particle.position);", + " sequencer[1].transform(particle.dst, particle.id);", + " }", + " particle.position.lerpVectors(particle.begin, particle.dst, ApplySequences.BEZIER.genValue((this.time - sequencer[0].a - delay) / (sequencer[0].b - sequencer[0].a)));", + " }", + " else if (this.time > sequencer[0].b + delay) {", + " particle.inMotion = false;", + " }", + " }", + " frameUpdate(delta) {", + " while (this.index + 1 < this.sequencers.length && this.time >= this.sequencers[this.index + 1][0].a) {", + " this.index++;", + " }", + " this.time += delta;", + " }", + " appendSequencer(range, sequencer) {", + " this.sequencers.push([range, sequencer]);", + " }", + " toJSON() {", + " return {", + " type: this.type,", + " delay: this.delay,", + " sequencers: this.sequencers.map(([range, sequencer]) => ({", + " range: range.toJSON(),", + " sequencer: sequencer.toJSON(),", + " })),", + " };", + " }", + " static fromJSON(json) {", + " const seq = new ApplySequences(json.delay);", + " json.sequencers.forEach((sequencerJson) => {", + " seq.sequencers.push([ValueGeneratorFromJSON(sequencerJson.range), SequencerFromJSON(sequencerJson.sequencer)]);", + " });", + " return seq;", + " }", + " clone() {", + " const applySequences = new ApplySequences(this.delay);", + " applySequences.sequencers = this.sequencers.map(seq => [seq[0].clone(), seq[1].clone()]);", + " return applySequences;", + " }", + "}", + "ApplySequences.BEZIER = new Bezier(0, 0, 1, 1);", + "", + "let physicsResolver;", + "function setPhysicsResolver(resolver) {", + " physicsResolver = resolver;", + "}", + "function getPhysicsResolver() {", + " return physicsResolver;", + "}", + "class ApplyCollision {", + " constructor(resolver, bounce) {", + " this.resolver = resolver;", + " this.bounce = bounce;", + " this.type = 'ApplyCollision';", + " this.tempV = new THREE.Vector3();", + " }", + " initialize(particle) {", + " }", + " update(particle, delta) {", + " if (this.resolver.resolve(particle.position, this.tempV)) {", + " particle.velocity.reflect(this.tempV).multiplyScalar(this.bounce);", + " }", + " }", + " frameUpdate(delta) {", + " }", + " toJSON() {", + " return {", + " type: this.type,", + " bounce: this.bounce,", + " };", + " }", + " static fromJSON(json) {", + " return new ApplyCollision(getPhysicsResolver(), json.bounce);", + " }", + " clone() {", + " return new ApplyCollision(this.resolver, this.bounce);", + " }", + " reset() {", + " }", + "}", + "", + "class ColorBySpeed {", + " constructor(color, speedRange) {", + " this.color = color;", + " this.speedRange = speedRange;", + " this.type = 'ColorBySpeed';", + " }", + " initialize(particle) {", + " if (this.color.type === 'memorizedFunction') {", + " particle.colorOverLifeMemory = {};", + " this.color.startGen(particle.colorOverLifeMemory);", + " }", + " }", + " update(particle, delta) {", + " const t = (particle.startSpeed - this.speedRange.a) / (this.speedRange.b - this.speedRange.a);", + " if (this.color.type === 'memorizedFunction') {", + " this.color.genColor(particle.color, t, particle.colorOverLifeMemory);", + " }", + " else {", + " this.color.genColor(particle.color, t);", + " }", + " particle.color.x *= particle.startColor.x;", + " particle.color.y *= particle.startColor.y;", + " particle.color.z *= particle.startColor.z;", + " particle.color.w *= particle.startColor.w;", + " }", + " frameUpdate(delta) { }", + " toJSON() {", + " return {", + " type: this.type,", + " color: this.color.toJSON(),", + " speedRange: this.speedRange.toJSON(),", + " };", + " }", + " static fromJSON(json) {", + " return new ColorBySpeed(ColorGeneratorFromJSON(json.color), IntervalValue.fromJSON(json.speedRange));", + " }", + " clone() {", + " return new ColorBySpeed(this.color.clone(), this.speedRange.clone());", + " }", + " reset() { }", + "}", + "", + "class SizeBySpeed {", + " initialize(particle) { }", + " constructor(size, speedRange) {", + " this.size = size;", + " this.speedRange = speedRange;", + " this.type = 'SizeBySpeed';", + " }", + " update(particle) {", + " const t = (particle.startSpeed - this.speedRange.a) / (this.speedRange.b - this.speedRange.a);", + " particle.size = particle.startSize * this.size.genValue(t);", + " }", + " toJSON() {", + " return {", + " type: this.type,", + " size: this.size.toJSON(),", + " speedRange: this.speedRange.toJSON(),", + " };", + " }", + " static fromJSON(json) {", + " return new SizeBySpeed(ValueGeneratorFromJSON(json.size), IntervalValue.fromJSON(json.speedRange));", + " }", + " frameUpdate(delta) { }", + " clone() {", + " return new SizeBySpeed(this.size.clone(), this.speedRange.clone());", + " }", + " reset() { }", + "}", + "", + "class RotationBySpeed {", + " constructor(angularVelocity, speedRange) {", + " this.angularVelocity = angularVelocity;", + " this.speedRange = speedRange;", + " this.type = 'RotationBySpeed';", + " this.tempQuat = new THREE.Quaternion();", + " }", + " initialize(particle) { }", + " update(particle, delta) {", + " const t = (particle.startSpeed - this.speedRange.a) / (this.speedRange.b - this.speedRange.a);", + " particle.rotation += delta * this.angularVelocity.genValue(t);", + " }", + " toJSON() {", + " return {", + " type: this.type,", + " angularVelocity: this.angularVelocity.toJSON(),", + " speedRange: this.speedRange.toJSON(),", + " };", + " }", + " static fromJSON(json) {", + " return new RotationBySpeed(ValueGeneratorFromJSON(json.angularVelocity), IntervalValue.fromJSON(json.speedRange));", + " }", + " frameUpdate(delta) { }", + " clone() {", + " return new RotationBySpeed(this.angularVelocity.clone(), this.speedRange.clone());", + " }", + " reset() { }", + "}", + "", + "class LimitSpeedOverLife {", + " initialize(particle) { }", + " constructor(speed, dampen) {", + " this.speed = speed;", + " this.dampen = dampen;", + " this.type = 'LimitSpeedOverLife';", + " }", + " update(particle, delta) {", + " let speed = particle.velocity.length();", + " let limit = this.speed.genValue(particle.age / particle.life);", + " if (speed > limit) {", + " const percent = (speed - limit) / speed;", + " particle.velocity.multiplyScalar(1 - percent * this.dampen * delta * 20);", + " }", + " }", + " toJSON() {", + " return {", + " type: this.type,", + " speed: this.speed.toJSON(),", + " dampen: this.dampen,", + " };", + " }", + " static fromJSON(json) {", + " return new LimitSpeedOverLife(ValueGeneratorFromJSON(json.speed), json.dampen);", + " }", + " frameUpdate(delta) { }", + " clone() {", + " return new LimitSpeedOverLife(this.speed.clone(), this.dampen);", + " }", + " reset() { }", + "}", + "", + "const BehaviorTypes = {", + " ApplyForce: {", + " type: 'ApplyForce',", + " constructor: ApplyForce,", + " params: [", + " ['direction', 'vec3'],", + " ['magnitude', 'value'],", + " ],", + " loadJSON: ApplyForce.fromJSON,", + " },", + " Noise: {", + " type: 'Noise',", + " constructor: Noise,", + " params: [", + " ['frequency', 'value'],", + " ['power', 'value'],", + " ['positionAmount', 'value'],", + " ['rotationAmount', 'value'],", + " ],", + " loadJSON: Noise.fromJSON,", + " },", + " TurbulenceField: {", + " type: 'TurbulenceField',", + " constructor: TurbulenceField,", + " params: [", + " ['scale', 'vec3'],", + " ['octaves', 'number'],", + " ['velocityMultiplier', 'vec3'],", + " ['timeScale', 'vec3'],", + " ],", + " loadJSON: TurbulenceField.fromJSON,", + " },", + " GravityForce: {", + " type: 'GravityForce',", + " constructor: GravityForce,", + " params: [", + " ['center', 'vec3'],", + " ['magnitude', 'number'],", + " ],", + " loadJSON: GravityForce.fromJSON,", + " },", + " ColorOverLife: {", + " type: 'ColorOverLife',", + " constructor: ColorOverLife,", + " params: [['color', 'colorFunc']],", + " loadJSON: ColorOverLife.fromJSON,", + " },", + " RotationOverLife: {", + " type: 'RotationOverLife',", + " constructor: RotationOverLife,", + " params: [['angularVelocity', 'valueFunc']],", + " loadJSON: RotationOverLife.fromJSON,", + " },", + " Rotation3DOverLife: {", + " type: 'Rotation3DOverLife',", + " constructor: Rotation3DOverLife,", + " params: [['angularVelocity', 'rotationFunc']],", + " loadJSON: Rotation3DOverLife.fromJSON,", + " },", + " SizeOverLife: {", + " type: 'SizeOverLife',", + " constructor: SizeOverLife,", + " params: [['size', 'valueFunc']],", + " loadJSON: SizeOverLife.fromJSON,", + " },", + " ColorBySpeed: {", + " type: 'ColorBySpeed',", + " constructor: ColorBySpeed,", + " params: [", + " ['color', 'colorFunc'],", + " ['speedRange', 'range'],", + " ],", + " loadJSON: ColorBySpeed.fromJSON,", + " },", + " RotationBySpeed: {", + " type: 'RotationBySpeed',", + " constructor: RotationBySpeed,", + " params: [", + " ['angularVelocity', 'valueFunc'],", + " ['speedRange', 'range'],", + " ],", + " loadJSON: RotationBySpeed.fromJSON,", + " },", + " SizeBySpeed: {", + " type: 'SizeBySpeed',", + " constructor: SizeBySpeed,", + " params: [", + " ['size', 'valueFunc'],", + " ['speedRange', 'range'],", + " ],", + " loadJSON: SizeBySpeed.fromJSON,", + " },", + " SpeedOverLife: {", + " type: 'SpeedOverLife',", + " constructor: SpeedOverLife,", + " params: [['speed', 'valueFunc']],", + " loadJSON: SpeedOverLife.fromJSON,", + " },", + " FrameOverLife: {", + " type: 'FrameOverLife',", + " constructor: FrameOverLife,", + " params: [['frame', 'valueFunc']],", + " loadJSON: FrameOverLife.fromJSON,", + " },", + " ForceOverLife: {", + " type: 'ForceOverLife',", + " constructor: ForceOverLife,", + " params: [", + " ['x', 'valueFunc'],", + " ['y', 'valueFunc'],", + " ['z', 'valueFunc'],", + " ],", + " loadJSON: ForceOverLife.fromJSON,", + " },", + " OrbitOverLife: {", + " type: 'OrbitOverLife',", + " constructor: OrbitOverLife,", + " params: [", + " ['orbitSpeed', 'valueFunc'],", + " ['axis', 'vec3'],", + " ],", + " loadJSON: OrbitOverLife.fromJSON,", + " },", + " WidthOverLength: {", + " type: 'WidthOverLength',", + " constructor: WidthOverLength,", + " params: [['width', 'valueFunc']],", + " loadJSON: WidthOverLength.fromJSON,", + " },", + " ChangeEmitDirection: {", + " type: 'ChangeEmitDirection',", + " constructor: ChangeEmitDirection,", + " params: [['angle', 'value']],", + " loadJSON: ChangeEmitDirection.fromJSON,", + " },", + " EmitSubParticleSystem: {", + " type: 'EmitSubParticleSystem',", + " constructor: EmitSubParticleSystem,", + " params: [", + " ['particleSystem', 'self'],", + " ['useVelocityAsBasis', 'boolean'],", + " ['subParticleSystem', 'particleSystem'],", + " ['mode', 'number'],", + " ['emitProbability', 'number'],", + " ],", + " loadJSON: EmitSubParticleSystem.fromJSON,", + " },", + " LimitSpeedOverLife: {", + " type: 'LimitSpeedOverLife',", + " constructor: LimitSpeedOverLife,", + " params: [", + " ['speed', 'valueFunc'],", + " ['dampen', 'number'],", + " ],", + " loadJSON: LimitSpeedOverLife.fromJSON,", + " },", + "};", + "function BehaviorFromJSON(json, particleSystem) {", + " return BehaviorTypes[json.type].loadJSON(json, particleSystem);", + "}", + "", + "let EmitterMode = void 0;", + "(function (EmitterMode) {", + " EmitterMode[EmitterMode[\"Random\"] = 0] = \"Random\";", + " EmitterMode[EmitterMode[\"Loop\"] = 1] = \"Loop\";", + " EmitterMode[EmitterMode[\"PingPong\"] = 2] = \"PingPong\";", + " EmitterMode[EmitterMode[\"Burst\"] = 3] = \"Burst\";", + "})(EmitterMode || (EmitterMode = {}));", + "function getValueFromEmitterMode(mode, currentValue, spread) {", + " let u;", + " if (EmitterMode.Random === mode) {", + " currentValue = Math.random();", + " }", + " if (spread > 0) {", + " u = Math.floor(currentValue / spread) * spread;", + " }", + " else {", + " u = currentValue;", + " }", + " switch (mode) {", + " case EmitterMode.Loop:", + " u = u % 1;", + " break;", + " case EmitterMode.PingPong:", + " u = Math.abs((u % 2) - 1);", + " break;", + " }", + " return u;", + "}", + "", + "class ConeEmitter {", + " constructor(parameters = {}) {", + " var _a, _b, _c, _d, _e, _f, _g;", + " this.type = 'cone';", + " this.currentValue = 0;", + " this.radius = (_a = parameters.radius) !== null && _a !== void 0 ? _a : 10;", + " this.arc = (_b = parameters.arc) !== null && _b !== void 0 ? _b : 2.0 * Math.PI;", + " this.thickness = (_c = parameters.thickness) !== null && _c !== void 0 ? _c : 1;", + " this.angle = (_d = parameters.angle) !== null && _d !== void 0 ? _d : Math.PI / 6;", + " this.mode = (_e = parameters.mode) !== null && _e !== void 0 ? _e : EmitterMode.Random;", + " this.spread = (_f = parameters.spread) !== null && _f !== void 0 ? _f : 0;", + " this.speed = (_g = parameters.speed) !== null && _g !== void 0 ? _g : new ConstantValue(1);", + " }", + " update(system, delta) {", + " if (EmitterMode.Random != this.mode) {", + " this.currentValue += this.speed.genValue(system.emissionState.time / system.duration) * delta;", + " }", + " }", + " initialize(p) {", + " const u = getValueFromEmitterMode(this.mode, this.currentValue, this.spread);", + " const rand = THREE.MathUtils.lerp(1 - this.thickness, 1, Math.random());", + " const theta = u * this.arc;", + " const r = Math.sqrt(rand);", + " const sinTheta = Math.sin(theta);", + " const cosTheta = Math.cos(theta);", + " p.position.x = r * cosTheta;", + " p.position.y = r * sinTheta;", + " p.position.z = 0;", + " const angle = this.angle * r;", + " p.velocity.set(0, 0, Math.cos(angle)).addScaledVector(p.position, Math.sin(angle)).multiplyScalar(p.startSpeed);", + " p.position.multiplyScalar(this.radius);", + " }", + " toJSON() {", + " return {", + " type: 'cone',", + " radius: this.radius,", + " arc: this.arc,", + " thickness: this.thickness,", + " angle: this.angle,", + " mode: this.mode,", + " spread: this.spread,", + " speed: this.speed.toJSON(),", + " };", + " }", + " static fromJSON(json) {", + " return new ConeEmitter({", + " radius: json.radius,", + " arc: json.arc,", + " thickness: json.thickness,", + " angle: json.angle,", + " mode: json.mode,", + " speed: json.speed ? ValueGeneratorFromJSON(json.speed) : undefined,", + " spread: json.spread,", + " });", + " }", + " clone() {", + " return new ConeEmitter({", + " radius: this.radius,", + " arc: this.arc,", + " thickness: this.thickness,", + " angle: this.angle,", + " mode: this.mode,", + " speed: this.speed.clone(),", + " spread: this.spread,", + " });", + " }", + "}", + "", + "class CircleEmitter {", + " constructor(parameters = {}) {", + " var _a, _b, _c, _d, _e, _f;", + " this.type = 'circle';", + " this.currentValue = 0;", + " this.radius = (_a = parameters.radius) !== null && _a !== void 0 ? _a : 10;", + " this.arc = (_b = parameters.arc) !== null && _b !== void 0 ? _b : 2.0 * Math.PI;", + " this.thickness = (_c = parameters.thickness) !== null && _c !== void 0 ? _c : 1;", + " this.mode = (_d = parameters.mode) !== null && _d !== void 0 ? _d : EmitterMode.Random;", + " this.spread = (_e = parameters.spread) !== null && _e !== void 0 ? _e : 0;", + " this.speed = (_f = parameters.speed) !== null && _f !== void 0 ? _f : new ConstantValue(1);", + " }", + " update(system, delta) {", + " this.currentValue += this.speed.genValue(system.emissionState.time / system.duration) * delta;", + " }", + " initialize(p) {", + " const u = getValueFromEmitterMode(this.mode, this.currentValue, this.spread);", + " const r = THREE.MathUtils.lerp(1 - this.thickness, 1, Math.random());", + " const theta = u * this.arc;", + " p.position.x = Math.cos(theta);", + " p.position.y = Math.sin(theta);", + " p.position.z = 0;", + " p.velocity.copy(p.position).multiplyScalar(p.startSpeed);", + " p.position.multiplyScalar(this.radius * r);", + " }", + " toJSON() {", + " return {", + " type: 'circle',", + " radius: this.radius,", + " arc: this.arc,", + " thickness: this.thickness,", + " mode: this.mode,", + " spread: this.spread,", + " speed: this.speed.toJSON(),", + " };", + " }", + " static fromJSON(json) {", + " return new CircleEmitter({", + " radius: json.radius,", + " arc: json.arc,", + " thickness: json.thickness,", + " mode: json.mode,", + " speed: json.speed ? ValueGeneratorFromJSON(json.speed) : undefined,", + " spread: json.spread,", + " });", + " }", + " clone() {", + " return new CircleEmitter({", + " radius: this.radius,", + " arc: this.arc,", + " thickness: this.thickness,", + " mode: this.mode,", + " speed: this.speed.clone(),", + " spread: this.spread,", + " });", + " }", + "}", + "", + "class DonutEmitter {", + " constructor(parameters = {}) {", + " var _a, _b, _c, _d, _e, _f, _g;", + " this.type = 'donut';", + " this.currentValue = 0;", + " this.radius = (_a = parameters.radius) !== null && _a !== void 0 ? _a : 10;", + " this.arc = (_b = parameters.arc) !== null && _b !== void 0 ? _b : 2.0 * Math.PI;", + " this.thickness = (_c = parameters.thickness) !== null && _c !== void 0 ? _c : 1;", + " this.donutRadius = (_d = parameters.donutRadius) !== null && _d !== void 0 ? _d : this.radius * 0.2;", + " this.mode = (_e = parameters.mode) !== null && _e !== void 0 ? _e : EmitterMode.Random;", + " this.spread = (_f = parameters.spread) !== null && _f !== void 0 ? _f : 0;", + " this.speed = (_g = parameters.speed) !== null && _g !== void 0 ? _g : new ConstantValue(1);", + " }", + " update(system, delta) {", + " if (EmitterMode.Random != this.mode) {", + " this.currentValue += this.speed.genValue(system.emissionState.time / system.duration) * delta;", + " }", + " }", + " initialize(p) {", + " const u = getValueFromEmitterMode(this.mode, this.currentValue, this.spread);", + " const v = Math.random();", + " const rand = THREE.MathUtils.lerp(1 - this.thickness, 1, Math.random());", + " const theta = u * this.arc;", + " const phi = v * Math.PI * 2;", + " const sinTheta = Math.sin(theta);", + " const cosTheta = Math.cos(theta);", + " p.position.x = this.radius * cosTheta;", + " p.position.y = this.radius * sinTheta;", + " p.position.z = 0;", + " p.velocity.z = this.donutRadius * rand * Math.sin(phi);", + " p.velocity.x = this.donutRadius * rand * Math.cos(phi) * cosTheta;", + " p.velocity.y = this.donutRadius * rand * Math.cos(phi) * sinTheta;", + " p.position.add(p.velocity);", + " p.velocity.normalize().multiplyScalar(p.startSpeed);", + " }", + " toJSON() {", + " return {", + " type: 'donut',", + " radius: this.radius,", + " arc: this.arc,", + " thickness: this.thickness,", + " donutRadius: this.donutRadius,", + " mode: this.mode,", + " spread: this.spread,", + " speed: this.speed.toJSON(),", + " };", + " }", + " static fromJSON(json) {", + " return new DonutEmitter({", + " radius: json.radius,", + " arc: json.arc,", + " thickness: json.thickness,", + " donutRadius: json.donutRadius,", + " mode: json.mode,", + " speed: json.speed ? ValueGeneratorFromJSON(json.speed) : undefined,", + " spread: json.spread,", + " });", + " }", + " clone() {", + " return new DonutEmitter({", + " radius: this.radius,", + " arc: this.arc,", + " thickness: this.thickness,", + " donutRadius: this.donutRadius,", + " mode: this.mode,", + " speed: this.speed.clone(),", + " spread: this.spread,", + " });", + " }", + "}", + "", + "class PointEmitter {", + " constructor() {", + " this.type = 'point';", + " }", + " update(system, delta) { }", + " initialize(p) {", + " const u = Math.random();", + " const v = Math.random();", + " const theta = u * Math.PI * 2;", + " const phi = Math.acos(2.0 * v - 1.0);", + " const r = Math.cbrt(Math.random());", + " const sinTheta = Math.sin(theta);", + " const cosTheta = Math.cos(theta);", + " const sinPhi = Math.sin(phi);", + " const cosPhi = Math.cos(phi);", + " p.velocity.x = r * sinPhi * cosTheta;", + " p.velocity.y = r * sinPhi * sinTheta;", + " p.velocity.z = r * cosPhi;", + " p.velocity.multiplyScalar(p.startSpeed);", + " p.position.setScalar(0);", + " }", + " toJSON() {", + " return {", + " type: 'point',", + " };", + " }", + " static fromJSON(json) {", + " return new PointEmitter();", + " }", + " clone() {", + " return new PointEmitter();", + " }", + "}", + "", + "class SphereEmitter {", + " constructor(parameters = {}) {", + " var _a, _b, _c, _d, _e, _f;", + " this.type = 'sphere';", + " this.currentValue = 0;", + " this.radius = (_a = parameters.radius) !== null && _a !== void 0 ? _a : 10;", + " this.arc = (_b = parameters.arc) !== null && _b !== void 0 ? _b : 2.0 * Math.PI;", + " this.thickness = (_c = parameters.thickness) !== null && _c !== void 0 ? _c : 1;", + " this.mode = (_d = parameters.mode) !== null && _d !== void 0 ? _d : EmitterMode.Random;", + " this.spread = (_e = parameters.spread) !== null && _e !== void 0 ? _e : 0;", + " this.speed = (_f = parameters.speed) !== null && _f !== void 0 ? _f : new ConstantValue(1);", + " }", + " update(system, delta) {", + " if (EmitterMode.Random != this.mode) {", + " this.currentValue += this.speed.genValue(system.emissionState.time / system.duration) * delta;", + " }", + " }", + " initialize(p) {", + " const u = getValueFromEmitterMode(this.mode, this.currentValue, this.spread);", + " const v = Math.random();", + " const rand = THREE.MathUtils.lerp(1 - this.thickness, 1, Math.random());", + " const theta = u * this.arc;", + " const phi = Math.acos(2.0 * v - 1.0);", + " const sinTheta = Math.sin(theta);", + " const cosTheta = Math.cos(theta);", + " const sinPhi = Math.sin(phi);", + " const cosPhi = Math.cos(phi);", + " p.position.x = sinPhi * cosTheta;", + " p.position.y = sinPhi * sinTheta;", + " p.position.z = cosPhi;", + " p.velocity.copy(p.position).multiplyScalar(p.startSpeed);", + " p.position.multiplyScalar(this.radius * rand);", + " }", + " toJSON() {", + " return {", + " type: 'sphere',", + " radius: this.radius,", + " arc: this.arc,", + " thickness: this.thickness,", + " mode: this.mode,", + " spread: this.spread,", + " speed: this.speed.toJSON(),", + " };", + " }", + " static fromJSON(json) {", + " return new SphereEmitter({", + " radius: json.radius,", + " arc: json.arc,", + " thickness: json.thickness,", + " mode: json.mode,", + " speed: json.speed ? ValueGeneratorFromJSON(json.speed) : undefined,", + " spread: json.spread,", + " });", + " }", + " clone() {", + " return new SphereEmitter({", + " radius: this.radius,", + " arc: this.arc,", + " thickness: this.thickness,", + " mode: this.mode,", + " speed: this.speed.clone(),", + " spread: this.spread,", + " });", + " }", + "}", + "", + "class HemisphereEmitter {", + " constructor(parameters = {}) {", + " var _a, _b, _c, _d, _e, _f;", + " this.type = 'sphere';", + " this.currentValue = 0;", + " this.radius = (_a = parameters.radius) !== null && _a !== void 0 ? _a : 10;", + " this.arc = (_b = parameters.arc) !== null && _b !== void 0 ? _b : 2.0 * Math.PI;", + " this.thickness = (_c = parameters.thickness) !== null && _c !== void 0 ? _c : 1;", + " this.mode = (_d = parameters.mode) !== null && _d !== void 0 ? _d : EmitterMode.Random;", + " this.spread = (_e = parameters.spread) !== null && _e !== void 0 ? _e : 0;", + " this.speed = (_f = parameters.speed) !== null && _f !== void 0 ? _f : new ConstantValue(1);", + " }", + " update(system, delta) {", + " if (EmitterMode.Random != this.mode) {", + " this.currentValue += this.speed.genValue(system.emissionState.time / system.duration) * delta;", + " }", + " }", + " initialize(p) {", + " const u = getValueFromEmitterMode(this.mode, this.currentValue, this.spread);", + " const v = Math.random();", + " const rand = THREE.MathUtils.lerp(1 - this.thickness, 1, Math.random());", + " const theta = u * this.arc;", + " const phi = Math.acos(v);", + " const sinTheta = Math.sin(theta);", + " const cosTheta = Math.cos(theta);", + " const sinPhi = Math.sin(phi);", + " const cosPhi = Math.cos(phi);", + " p.position.x = sinPhi * cosTheta;", + " p.position.y = sinPhi * sinTheta;", + " p.position.z = cosPhi;", + " p.velocity.copy(p.position).multiplyScalar(p.startSpeed);", + " p.position.multiplyScalar(this.radius * rand);", + " }", + " toJSON() {", + " return {", + " type: 'hemisphere',", + " radius: this.radius,", + " arc: this.arc,", + " thickness: this.thickness,", + " mode: this.mode,", + " spread: this.spread,", + " speed: this.speed.toJSON(),", + " };", + " }", + " static fromJSON(json) {", + " return new HemisphereEmitter({", + " radius: json.radius,", + " arc: json.arc,", + " thickness: json.thickness,", + " mode: json.mode,", + " speed: json.speed ? ValueGeneratorFromJSON(json.speed) : undefined,", + " spread: json.spread,", + " });", + " }", + " clone() {", + " return new HemisphereEmitter({", + " radius: this.radius,", + " arc: this.arc,", + " thickness: this.thickness,", + " mode: this.mode,", + " speed: this.speed.clone(),", + " spread: this.spread,", + " });", + " }", + "}", + "", + "class GridEmitter {", + " constructor(parameters = {}) {", + " var _a, _b, _c, _d;", + " this.type = 'grid';", + " this.width = (_a = parameters.width) !== null && _a !== void 0 ? _a : 1;", + " this.height = (_b = parameters.height) !== null && _b !== void 0 ? _b : 1;", + " this.column = (_c = parameters.column) !== null && _c !== void 0 ? _c : 10;", + " this.row = (_d = parameters.row) !== null && _d !== void 0 ? _d : 10;", + " }", + " initialize(p) {", + " const r = Math.floor(Math.random() * this.row);", + " const c = Math.floor(Math.random() * this.column);", + " p.position.x = (c * this.width) / this.column - this.width / 2;", + " p.position.y = (r * this.height) / this.row - this.height / 2;", + " p.position.z = 0;", + " p.velocity.set(0, 0, p.startSpeed);", + " }", + " toJSON() {", + " return {", + " type: 'grid',", + " width: this.width,", + " height: this.height,", + " column: this.column,", + " row: this.row,", + " };", + " }", + " static fromJSON(json) {", + " return new GridEmitter(json);", + " }", + " clone() {", + " return new GridEmitter({", + " width: this.width,", + " height: this.height,", + " column: this.column,", + " row: this.row,", + " });", + " }", + " update(system, delta) { }", + "}", + "", + "class MeshSurfaceEmitter {", + " get geometry() {", + " return this._geometry;", + " }", + " set geometry(geometry) {", + " this._geometry = geometry;", + " if (geometry === undefined) {", + " return;", + " }", + " if (typeof geometry === 'string') {", + " return;", + " }", + " const tri = new THREE.Triangle();", + " this._triangleIndexToArea.length = 0;", + " let area = 0;", + " if (!geometry.getIndex()) {", + " return;", + " }", + " const array = geometry.getIndex().array;", + " const triCount = array.length / 3;", + " this._triangleIndexToArea.push(0);", + " for (let i = 0; i < triCount; i++) {", + " tri.setFromAttributeAndIndices(geometry.getAttribute('position'), array[i * 3], array[i * 3 + 1], array[i * 3 + 2]);", + " area += tri.getArea();", + " this._triangleIndexToArea.push(area);", + " }", + " geometry.userData.triangleIndexToArea = this._triangleIndexToArea;", + " }", + " constructor(geometry) {", + " this.type = 'mesh_surface';", + " this._triangleIndexToArea = [];", + " this._tempA = new THREE.Vector3();", + " this._tempB = new THREE.Vector3();", + " this._tempC = new THREE.Vector3();", + " if (!geometry) {", + " return;", + " }", + " this.geometry = geometry;", + " }", + " initialize(p) {", + " const geometry = this._geometry;", + " if (!geometry || geometry.getIndex() === null) {", + " p.position.set(0, 0, 0);", + " p.velocity.set(0, 0, 1).multiplyScalar(p.startSpeed);", + " return;", + " }", + " const triCount = this._triangleIndexToArea.length - 1;", + " let left = 0, right = triCount;", + " const target = Math.random() * this._triangleIndexToArea[triCount];", + " while (left + 1 < right) {", + " const mid = Math.floor((left + right) / 2);", + " if (target < this._triangleIndexToArea[mid]) {", + " right = mid;", + " }", + " else {", + " left = mid;", + " }", + " }", + " let u1 = Math.random();", + " let u2 = Math.random();", + " if (u1 + u2 > 1) {", + " u1 = 1 - u1;", + " u2 = 1 - u2;", + " }", + " const index1 = geometry.getIndex().array[left * 3];", + " const index2 = geometry.getIndex().array[left * 3 + 1];", + " const index3 = geometry.getIndex().array[left * 3 + 2];", + " const positionBuffer = geometry.getAttribute('position');", + " this._tempA.fromBufferAttribute(positionBuffer, index1);", + " this._tempB.fromBufferAttribute(positionBuffer, index2);", + " this._tempC.fromBufferAttribute(positionBuffer, index3);", + " this._tempB.sub(this._tempA);", + " this._tempC.sub(this._tempA);", + " this._tempA.addScaledVector(this._tempB, u1).addScaledVector(this._tempC, u2);", + " p.position.copy(this._tempA);", + " this._tempA.copy(this._tempB).cross(this._tempC).normalize();", + " p.velocity.copy(this._tempA).normalize().multiplyScalar(p.startSpeed);", + " }", + " toJSON() {", + " return {", + " type: 'mesh_surface',", + " mesh: this._geometry ? this._geometry.uuid : '',", + " };", + " }", + " static fromJSON(json, meta) {", + " return new MeshSurfaceEmitter(meta.geometries[json.geometry]);", + " }", + " clone() {", + " return new MeshSurfaceEmitter(this._geometry);", + " }", + " update(system, delta) { }", + "}", + "", + "const EmitterShapes = {", + " circle: {", + " type: 'circle',", + " params: [", + " ['radius', 'number'],", + " ['arc', 'radian'],", + " ['thickness', 'number'],", + " ['mode', 'emitterMode'],", + " ['spread', 'number'],", + " ['speed', 'valueFunc'],", + " ],", + " constructor: CircleEmitter,", + " loadJSON: CircleEmitter.fromJSON,", + " },", + " cone: {", + " type: 'cone',", + " params: [", + " ['radius', 'number'],", + " ['arc', 'radian'],", + " ['thickness', 'number'],", + " ['angle', 'radian'],", + " ['mode', 'emitterMode'],", + " ['spread', 'number'],", + " ['speed', 'valueFunc'],", + " ],", + " constructor: ConeEmitter,", + " loadJSON: ConeEmitter.fromJSON,", + " },", + " donut: {", + " type: 'donut',", + " params: [", + " ['radius', 'number'],", + " ['arc', 'radian'],", + " ['thickness', 'number'],", + " ['donutRadius', 'number'],", + " ['mode', 'emitterMode'],", + " ['spread', 'number'],", + " ['speed', 'valueFunc'],", + " ],", + " constructor: DonutEmitter,", + " loadJSON: DonutEmitter.fromJSON,", + " },", + " point: { type: 'point', params: [], constructor: PointEmitter, loadJSON: PointEmitter.fromJSON },", + " sphere: {", + " type: 'sphere',", + " params: [", + " ['radius', 'number'],", + " ['arc', 'radian'],", + " ['thickness', 'number'],", + " ['angle', 'radian'],", + " ['mode', 'emitterMode'],", + " ['spread', 'number'],", + " ['speed', 'valueFunc'],", + " ],", + " constructor: SphereEmitter,", + " loadJSON: SphereEmitter.fromJSON,", + " },", + " hemisphere: {", + " type: 'hemisphere',", + " params: [", + " ['radius', 'number'],", + " ['arc', 'radian'],", + " ['thickness', 'number'],", + " ['angle', 'radian'],", + " ['mode', 'emitterMode'],", + " ['spread', 'number'],", + " ['speed', 'valueFunc'],", + " ],", + " constructor: HemisphereEmitter,", + " loadJSON: HemisphereEmitter.fromJSON,", + " },", + " grid: {", + " type: 'grid',", + " params: [", + " ['width', 'number'],", + " ['height', 'number'],", + " ['rows', 'number'],", + " ['column', 'number'],", + " ],", + " constructor: GridEmitter,", + " loadJSON: GridEmitter.fromJSON,", + " },", + " mesh_surface: {", + " type: 'mesh_surface',", + " params: [['geometry', 'geometry']],", + " constructor: MeshSurfaceEmitter,", + " loadJSON: MeshSurfaceEmitter.fromJSON,", + " },", + "};", + "function EmitterFromJSON(json, meta) {", + " return EmitterShapes[json.type].loadJSON(json, meta);", + "}", + "", + "let RenderMode = void 0;", + "(function (RenderMode) {", + " RenderMode[RenderMode[\"BillBoard\"] = 0] = \"BillBoard\";", + " RenderMode[RenderMode[\"StretchedBillBoard\"] = 1] = \"StretchedBillBoard\";", + " RenderMode[RenderMode[\"Mesh\"] = 2] = \"Mesh\";", + " RenderMode[RenderMode[\"Trail\"] = 3] = \"Trail\";", + " RenderMode[RenderMode[\"HorizontalBillBoard\"] = 4] = \"HorizontalBillBoard\";", + " RenderMode[RenderMode[\"VerticalBillBoard\"] = 5] = \"VerticalBillBoard\";", + "})(RenderMode || (RenderMode = {}));", + "class VFXBatch extends THREE.Mesh {", + " constructor(settings) {", + " super();", + " this.type = 'VFXBatch';", + " this.maxParticles = 1000;", + " this.systems = new Set();", + " const layers = new THREE.Layers();", + " layers.mask = settings.layers.mask;", + " const newMat = settings.material.clone();", + " newMat.defines = {};", + " Object.assign(newMat.defines, settings.material.defines);", + " this.settings = {", + " instancingGeometry: settings.instancingGeometry,", + " renderMode: settings.renderMode,", + " renderOrder: settings.renderOrder,", + " material: newMat,", + " uTileCount: settings.uTileCount,", + " vTileCount: settings.vTileCount,", + " layers: layers,", + " };", + " this.frustumCulled = false;", + " this.renderOrder = this.settings.renderOrder;", + " }", + " addSystem(system) {", + " this.systems.add(system);", + " }", + " removeSystem(system) {", + " this.systems.delete(system);", + " }", + "}", + "", + "const UP = new THREE.Vector3(0, 0, 1);", + "const tempQ$1 = new THREE.Quaternion();", + "const tempV$1 = new THREE.Vector3();", + "const tempV2$1 = new THREE.Vector3();", + "new THREE.Vector3();", + "const PREWARM_FPS$1 = 60;", + "const DEFAULT_GEOMETRY$1 = new THREE.PlaneGeometry(1, 1, 1, 1);", + "class ParticleSystem {", + " set time(time) {", + " this.emissionState.time = time;", + " }", + " get time() {", + " return this.emissionState.time;", + " }", + " get layers() {", + " return this.rendererSettings.layers;", + " }", + " get texture() {", + " return this.rendererSettings.material.map;", + " }", + " set texture(texture) {", + " this.rendererSettings.material.map = texture;", + " this.neededToUpdateRender = true;", + " }", + " get material() {", + " return this.rendererSettings.material;", + " }", + " set material(material) {", + " this.rendererSettings.material = material;", + " this.neededToUpdateRender = true;", + " }", + " get uTileCount() {", + " return this.rendererSettings.uTileCount;", + " }", + " set uTileCount(u) {", + " this.rendererSettings.uTileCount = u;", + " this.neededToUpdateRender = true;", + " }", + " get vTileCount() {", + " return this.rendererSettings.vTileCount;", + " }", + " set vTileCount(v) {", + " this.rendererSettings.vTileCount = v;", + " this.neededToUpdateRender = true;", + " }", + " get instancingGeometry() {", + " return this.rendererSettings.instancingGeometry;", + " }", + " set instancingGeometry(geometry) {", + " this.restart();", + " this.particles.length = 0;", + " this.rendererSettings.instancingGeometry = geometry;", + " this.neededToUpdateRender = true;", + " }", + " get renderMode() {", + " return this.rendererSettings.renderMode;", + " }", + " set renderMode(renderMode) {", + " if ((this.rendererSettings.renderMode != RenderMode.Trail && renderMode === RenderMode.Trail) ||", + " (this.rendererSettings.renderMode == RenderMode.Trail && renderMode !== RenderMode.Trail)) {", + " this.restart();", + " this.particles.length = 0;", + " }", + " if (this.rendererSettings.renderMode !== renderMode) {", + " switch (renderMode) {", + " case RenderMode.Trail:", + " this.rendererEmitterSettings = {", + " startLength: new ConstantValue(30),", + " followLocalOrigin: false,", + " };", + " break;", + " case RenderMode.Mesh:", + " this.rendererEmitterSettings = {", + " geometry: new THREE.PlaneGeometry(1, 1),", + " };", + " this.startRotation = new AxisAngleGenerator(new THREE.Vector3(0, 1, 0), new ConstantValue(0));", + " break;", + " case RenderMode.StretchedBillBoard:", + " this.rendererEmitterSettings = { speedFactor: 0, lengthFactor: 2 };", + " if (this.rendererSettings.renderMode === RenderMode.Mesh) {", + " this.startRotation = new ConstantValue(0);", + " }", + " break;", + " case RenderMode.BillBoard:", + " case RenderMode.VerticalBillBoard:", + " case RenderMode.HorizontalBillBoard:", + " this.rendererEmitterSettings = {};", + " if (this.rendererSettings.renderMode === RenderMode.Mesh) {", + " this.startRotation = new ConstantValue(0);", + " }", + " break;", + " }", + " }", + " this.rendererSettings.renderMode = renderMode;", + " this.neededToUpdateRender = true;", + " }", + " get renderOrder() {", + " return this.rendererSettings.renderOrder;", + " }", + " set renderOrder(renderOrder) {", + " this.rendererSettings.renderOrder = renderOrder;", + " this.neededToUpdateRender = true;", + " }", + " get blending() {", + " return this.rendererSettings.material.blending;", + " }", + " set blending(blending) {", + " this.rendererSettings.material.blending = blending;", + " this.neededToUpdateRender = true;", + " }", + " constructor(parameters) {", + " var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m, _o, _p, _q, _r, _s, _t, _u, _v, _w, _x;", + " this.temp = new THREE.Vector3();", + " this.travelDistance = 0;", + " this.normalMatrix = new THREE.Matrix3();", + " this.firstTimeUpdate = true;", + " this.autoDestroy = parameters.autoDestroy === undefined ? false : parameters.autoDestroy;", + " this.duration = (_a = parameters.duration) !== null && _a !== void 0 ? _a : 1;", + " this.looping = parameters.looping === undefined ? true : parameters.looping;", + " this.prewarm = parameters.prewarm === undefined ? false : parameters.prewarm;", + " this.startLife = (_b = parameters.startLife) !== null && _b !== void 0 ? _b : new ConstantValue(5);", + " this.startSpeed = (_c = parameters.startSpeed) !== null && _c !== void 0 ? _c : new ConstantValue(0);", + " this.startRotation = (_d = parameters.startRotation) !== null && _d !== void 0 ? _d : new ConstantValue(0);", + " this.startSize = (_e = parameters.startSize) !== null && _e !== void 0 ? _e : new ConstantValue(1);", + " this.startColor = (_f = parameters.startColor) !== null && _f !== void 0 ? _f : new ConstantColor(new THREE.Vector4(1, 1, 1, 1));", + " this.emissionOverTime = (_g = parameters.emissionOverTime) !== null && _g !== void 0 ? _g : new ConstantValue(10);", + " this.emissionOverDistance = (_h = parameters.emissionOverDistance) !== null && _h !== void 0 ? _h : new ConstantValue(0);", + " this.emissionBursts = (_j = parameters.emissionBursts) !== null && _j !== void 0 ? _j : [];", + " this.onlyUsedByOther = (_k = parameters.onlyUsedByOther) !== null && _k !== void 0 ? _k : false;", + " this.emitterShape = (_l = parameters.shape) !== null && _l !== void 0 ? _l : new SphereEmitter();", + " this.behaviors = (_m = parameters.behaviors) !== null && _m !== void 0 ? _m : new Array();", + " this.worldSpace = (_o = parameters.worldSpace) !== null && _o !== void 0 ? _o : false;", + " this.rendererEmitterSettings = (_p = parameters.rendererEmitterSettings) !== null && _p !== void 0 ? _p : {};", + " if (parameters.renderMode === RenderMode.StretchedBillBoard) {", + " const stretchedBillboardSettings = this.rendererEmitterSettings;", + " if (parameters.speedFactor !== undefined) {", + " stretchedBillboardSettings.speedFactor = parameters.speedFactor;", + " }", + " stretchedBillboardSettings.speedFactor = (_q = stretchedBillboardSettings.speedFactor) !== null && _q !== void 0 ? _q : 0;", + " stretchedBillboardSettings.lengthFactor = (_r = stretchedBillboardSettings.lengthFactor) !== null && _r !== void 0 ? _r : 0;", + " }", + " this.rendererSettings = {", + " instancingGeometry: (_s = parameters.instancingGeometry) !== null && _s !== void 0 ? _s : DEFAULT_GEOMETRY$1,", + " renderMode: (_t = parameters.renderMode) !== null && _t !== void 0 ? _t : RenderMode.BillBoard,", + " renderOrder: (_u = parameters.renderOrder) !== null && _u !== void 0 ? _u : 0,", + " material: parameters.material,", + " uTileCount: (_v = parameters.uTileCount) !== null && _v !== void 0 ? _v : 1,", + " vTileCount: (_w = parameters.vTileCount) !== null && _w !== void 0 ? _w : 1,", + " layers: (_x = parameters.layers) !== null && _x !== void 0 ? _x : new THREE.Layers(),", + " };", + " this.neededToUpdateRender = true;", + " this.particles = new Array();", + " this.startTileIndex = parameters.startTileIndex || new ConstantValue(0);", + " this.emitter = new ParticleEmitter(this);", + " this.paused = false;", + " this.particleNum = 0;", + " this.emissionState = {", + " burstIndex: 0,", + " burstWaveIndex: 0,", + " time: 0,", + " waitEmiting: 0,", + " travelDistance: 0,", + " };", + " this.emitEnded = false;", + " this.markForDestroy = false;", + " this.prewarmed = false;", + " }", + " pause() {", + " this.paused = true;", + " }", + " play() {", + " this.paused = false;", + " }", + " spawn(count, emissionState, matrix) {", + " tempQ$1.setFromRotationMatrix(matrix);", + " const translation = tempV$1;", + " const quaternion = tempQ$1;", + " const scale = tempV2$1;", + " matrix.decompose(translation, quaternion, scale);", + " for (let i = 0; i < count; i++) {", + " this.particleNum++;", + " while (this.particles.length < this.particleNum) {", + " if (this.rendererSettings.renderMode === RenderMode.Trail) {", + " this.particles.push(new TrailParticle());", + " }", + " else {", + " this.particles.push(new SpriteParticle());", + " }", + " }", + " const particle = this.particles[this.particleNum - 1];", + " particle.speedModifier = 1;", + " this.startColor.genColor(particle.startColor, this.emissionState.time, {});", + " particle.color.copy(particle.startColor);", + " particle.startSpeed = this.startSpeed.genValue(emissionState.time / this.duration);", + " particle.life = this.startLife.genValue(emissionState.time / this.duration);", + " particle.age = 0;", + " particle.startSize = this.startSize.genValue(emissionState.time / this.duration);", + " particle.uvTile = Math.floor(this.startTileIndex.genValue() + 0.001);", + " particle.size = particle.startSize;", + " if (this.rendererSettings.renderMode === RenderMode.Mesh ||", + " this.rendererSettings.renderMode === RenderMode.BillBoard ||", + " this.rendererSettings.renderMode === RenderMode.VerticalBillBoard ||", + " this.rendererSettings.renderMode === RenderMode.HorizontalBillBoard ||", + " this.rendererSettings.renderMode === RenderMode.StretchedBillBoard) {", + " const sprite = particle;", + " if (this.rendererSettings.renderMode === RenderMode.Mesh) {", + " if (!(sprite.rotation instanceof THREE.Quaternion)) {", + " sprite.rotation = new THREE.Quaternion();", + " }", + " if (this.startRotation.type === 'rotation') {", + " this.startRotation.genValue(sprite.rotation, emissionState.time / this.duration);", + " }", + " else {", + " sprite.rotation.setFromAxisAngle(UP, this.startRotation.genValue((emissionState.time / this.duration)));", + " }", + " }", + " else {", + " if (this.startRotation.type === 'rotation') {", + " sprite.rotation = 0;", + " }", + " else {", + " sprite.rotation = this.startRotation.genValue(emissionState.time / this.duration);", + " }", + " }", + " }", + " else if (this.rendererSettings.renderMode === RenderMode.Trail) {", + " const trail = particle;", + " trail.length = this.rendererEmitterSettings.startLength.genValue(emissionState.time / this.duration);", + " }", + " this.emitterShape.initialize(particle);", + " if (this.rendererSettings.renderMode === RenderMode.Trail &&", + " this.rendererEmitterSettings.followLocalOrigin) {", + " const trail = particle;", + " trail.localPosition = new THREE.Vector3().copy(trail.position);", + " }", + " if (this.worldSpace) {", + " particle.position.applyMatrix4(matrix);", + " particle.startSize =", + " (particle.startSize * (Math.abs(scale.x) + Math.abs(scale.y) + Math.abs(scale.z))) / 3;", + " particle.size = particle.startSize;", + " particle.velocity.multiply(scale).applyMatrix3(this.normalMatrix);", + " if (particle.rotation && particle.rotation instanceof THREE.Quaternion) {", + " particle.rotation.multiplyQuaternions(tempQ$1, particle.rotation);", + " }", + " }", + " else {", + " if (this.onlyUsedByOther) {", + " particle.parentMatrix = matrix;", + " }", + " }", + " for (let j = 0; j < this.behaviors.length; j++) {", + " this.behaviors[j].initialize(particle);", + " }", + " }", + " }", + " endEmit() {", + " this.emitEnded = true;", + " if (this.autoDestroy) {", + " this.markForDestroy = true;", + " }", + " }", + " dispose() {", + " if (this._renderer)", + " this._renderer.deleteSystem(this);", + " this.emitter.dispose();", + " if (this.emitter.parent)", + " this.emitter.parent.remove(this.emitter);", + " }", + " restart() {", + " this.paused = false;", + " this.particleNum = 0;", + " this.emissionState.burstIndex = 0;", + " this.emissionState.burstWaveIndex = 0;", + " this.emissionState.time = 0;", + " this.emissionState.waitEmiting = 0;", + " this.behaviors.forEach((behavior) => {", + " behavior.reset();", + " });", + " this.emitEnded = false;", + " this.markForDestroy = false;", + " this.prewarmed = false;", + " }", + " update(delta) {", + " if (this.paused)", + " return;", + " let currentParent = this.emitter;", + " while (currentParent.parent) {", + " currentParent = currentParent.parent;", + " }", + " if (currentParent.type !== 'Scene') {", + " this.dispose();", + " return;", + " }", + " if (this.firstTimeUpdate) {", + " this.firstTimeUpdate = false;", + " this.emitter.updateWorldMatrix(true, false);", + " }", + " if (this.emitEnded && this.particleNum === 0) {", + " if (this.markForDestroy && this.emitter.parent)", + " this.dispose();", + " return;", + " }", + " if (this.looping && this.prewarm && !this.prewarmed) {", + " this.prewarmed = true;", + " for (let i = 0; i < this.duration * PREWARM_FPS$1; i++) {", + " this.update(1.0 / PREWARM_FPS$1);", + " }", + " }", + " if (delta > 0.1) {", + " delta = 0.1;", + " }", + " if (this.neededToUpdateRender) {", + " if (this._renderer)", + " this._renderer.updateSystem(this);", + " this.neededToUpdateRender = false;", + " }", + " if (!this.onlyUsedByOther) {", + " this.emit(delta, this.emissionState, this.emitter.matrixWorld);", + " }", + " this.emitterShape.update(this, delta);", + " for (let j = 0; j < this.behaviors.length; j++) {", + " for (let i = 0; i < this.particleNum; i++) {", + " if (!this.particles[i].died) {", + " this.behaviors[j].update(this.particles[i], delta);", + " }", + " }", + " this.behaviors[j].frameUpdate(delta);", + " }", + " for (let i = 0; i < this.particleNum; i++) {", + " if (this.rendererEmitterSettings.followLocalOrigin &&", + " this.particles[i].localPosition) {", + " this.particles[i].position.copy(this.particles[i].localPosition);", + " if (this.particles[i].parentMatrix) {", + " this.particles[i].position.applyMatrix4(this.particles[i].parentMatrix);", + " }", + " else {", + " this.particles[i].position.applyMatrix4(this.emitter.matrixWorld);", + " }", + " }", + " else {", + " this.particles[i].position.addScaledVector(this.particles[i].velocity, delta * this.particles[i].speedModifier);", + " }", + " this.particles[i].age += delta;", + " }", + " if (this.rendererSettings.renderMode === RenderMode.Trail) {", + " for (let i = 0; i < this.particleNum; i++) {", + " const particle = this.particles[i];", + " particle.update();", + " }", + " }", + " for (let i = 0; i < this.particleNum; i++) {", + " const particle = this.particles[i];", + " if (particle.died && (!(particle instanceof TrailParticle) || particle.previous.length === 0)) {", + " this.particles[i] = this.particles[this.particleNum - 1];", + " this.particles[this.particleNum - 1] = particle;", + " this.particleNum--;", + " i--;", + " }", + " }", + " }", + " emit(delta, emissionState, emitterMatrix) {", + " if (emissionState.time > this.duration) {", + " if (this.looping) {", + " emissionState.time -= this.duration;", + " emissionState.burstIndex = 0;", + " this.behaviors.forEach((behavior) => {", + " behavior.reset();", + " });", + " }", + " else {", + " if (!this.emitEnded && !this.onlyUsedByOther) {", + " this.endEmit();", + " }", + " }", + " }", + " this.normalMatrix.getNormalMatrix(emitterMatrix);", + " const totalSpawn = Math.ceil(emissionState.waitEmiting);", + " this.spawn(totalSpawn, emissionState, emitterMatrix);", + " emissionState.waitEmiting -= totalSpawn;", + " while (emissionState.burstIndex < this.emissionBursts.length &&", + " this.emissionBursts[emissionState.burstIndex].time <= emissionState.time) {", + " if (Math.random() < this.emissionBursts[emissionState.burstIndex].probability) {", + " const count = this.emissionBursts[emissionState.burstIndex].count.genValue(this.time);", + " this.spawn(count, emissionState, emitterMatrix);", + " }", + " emissionState.burstIndex++;", + " }", + " if (!this.emitEnded) {", + " emissionState.waitEmiting += delta * this.emissionOverTime.genValue(emissionState.time / this.duration);", + " if (emissionState.previousWorldPos != undefined) {", + " this.temp.set(emitterMatrix.elements[12], emitterMatrix.elements[13], emitterMatrix.elements[14]);", + " emissionState.travelDistance += emissionState.previousWorldPos.distanceTo(this.temp);", + " const emitPerMeter = this.emissionOverDistance.genValue(emissionState.time / this.duration);", + " if (emissionState.travelDistance * emitPerMeter > 0) {", + " const count = Math.floor(emissionState.travelDistance * emitPerMeter);", + " emissionState.travelDistance -= count / emitPerMeter;", + " emissionState.waitEmiting += count;", + " }", + " }", + " }", + " if (emissionState.previousWorldPos === undefined)", + " emissionState.previousWorldPos = new THREE.Vector3();", + " emissionState.previousWorldPos.set(emitterMatrix.elements[12], emitterMatrix.elements[13], emitterMatrix.elements[14]);", + " emissionState.time += delta;", + " }", + " toJSON(meta, options = {}) {", + " const isRootObject = meta === undefined || typeof meta === 'string';", + " if (isRootObject) {", + " meta = {", + " geometries: {},", + " materials: {},", + " textures: {},", + " images: {},", + " shapes: {},", + " skeletons: {},", + " animations: {},", + " nodes: {},", + " };", + " }", + " meta.materials[this.rendererSettings.material.uuid] = this.rendererSettings.material.toJSON(meta);", + " if (options.useUrlForImage) {", + " if (this.texture.source !== undefined) {", + " const image = this.texture.source;", + " meta.images[image.uuid] = {", + " uuid: image.uuid,", + " url: this.texture.image.url,", + " };", + " }", + " }", + " let rendererSettingsJSON;", + " if (this.renderMode === RenderMode.Trail) {", + " rendererSettingsJSON = {", + " startLength: this.rendererEmitterSettings.startLength.toJSON(),", + " followLocalOrigin: this.rendererEmitterSettings.followLocalOrigin,", + " };", + " }", + " else if (this.renderMode === RenderMode.Mesh) {", + " rendererSettingsJSON = {};", + " }", + " else if (this.renderMode === RenderMode.StretchedBillBoard) {", + " rendererSettingsJSON = {", + " speedFactor: this.rendererEmitterSettings.speedFactor,", + " lengthFactor: this.rendererEmitterSettings.lengthFactor,", + " };", + " }", + " else {", + " rendererSettingsJSON = {};", + " }", + " const geometry = this.rendererSettings.instancingGeometry;", + " if (meta.geometries && !meta.geometries[geometry.uuid]) {", + " meta.geometries[geometry.uuid] = geometry.toJSON();", + " }", + " return {", + " version: '2.0',", + " autoDestroy: this.autoDestroy,", + " looping: this.looping,", + " prewarm: this.prewarm,", + " duration: this.duration,", + " shape: this.emitterShape.toJSON(),", + " startLife: this.startLife.toJSON(),", + " startSpeed: this.startSpeed.toJSON(),", + " startRotation: this.startRotation.toJSON(),", + " startSize: this.startSize.toJSON(),", + " startColor: this.startColor.toJSON(),", + " emissionOverTime: this.emissionOverTime.toJSON(),", + " emissionOverDistance: this.emissionOverDistance.toJSON(),", + " emissionBursts: this.emissionBursts.map((burst) => ({", + " time: burst.time,", + " count: burst.count.toJSON(),", + " probability: burst.probability,", + " interval: burst.interval,", + " cycle: burst.cycle,", + " })),", + " onlyUsedByOther: this.onlyUsedByOther,", + " instancingGeometry: this.rendererSettings.instancingGeometry.uuid,", + " renderOrder: this.renderOrder,", + " renderMode: this.renderMode,", + " rendererEmitterSettings: rendererSettingsJSON,", + " material: this.rendererSettings.material.uuid,", + " layers: this.layers.mask,", + " startTileIndex: this.startTileIndex.toJSON(),", + " uTileCount: this.uTileCount,", + " vTileCount: this.vTileCount,", + " behaviors: this.behaviors.map((behavior) => behavior.toJSON()),", + " worldSpace: this.worldSpace,", + " };", + " }", + " static fromJSON(json, meta, dependencies) {", + " var _a, _b;", + " const shape = EmitterFromJSON(json.shape, meta);", + " let rendererEmitterSettings;", + " if (json.renderMode === RenderMode.Trail) {", + " let trailSettings = json.rendererEmitterSettings;", + " rendererEmitterSettings = {", + " startLength: trailSettings.startLength != undefined", + " ? ValueGeneratorFromJSON(trailSettings.startLength)", + " : new ConstantValue(30),", + " followLocalOrigin: trailSettings.followLocalOrigin,", + " };", + " }", + " else if (json.renderMode === RenderMode.Mesh) {", + " rendererEmitterSettings = {};", + " }", + " else if (json.renderMode === RenderMode.StretchedBillBoard) {", + " rendererEmitterSettings = json.rendererEmitterSettings;", + " if (json.speedFactor != undefined) {", + " rendererEmitterSettings.speedFactor = json.speedFactor;", + " }", + " }", + " else {", + " rendererEmitterSettings = {};", + " }", + " const layers = new THREE.Layers();", + " if (json.layers) {", + " layers.mask = json.layers;", + " }", + " const ps = new ParticleSystem({", + " autoDestroy: json.autoDestroy,", + " looping: json.looping,", + " prewarm: json.prewarm,", + " duration: json.duration,", + " shape: shape,", + " startLife: ValueGeneratorFromJSON(json.startLife),", + " startSpeed: ValueGeneratorFromJSON(json.startSpeed),", + " startRotation: GeneratorFromJSON(json.startRotation),", + " startSize: ValueGeneratorFromJSON(json.startSize),", + " startColor: ColorGeneratorFromJSON(json.startColor),", + " emissionOverTime: ValueGeneratorFromJSON(json.emissionOverTime),", + " emissionOverDistance: ValueGeneratorFromJSON(json.emissionOverDistance),", + " emissionBursts: (_a = json.emissionBursts) === null || _a === void 0 ? void 0 : _a.map((burst) => ({", + " time: burst.time,", + " count: typeof burst.count === 'number'", + " ? new ConstantValue(burst.count)", + " : ValueGeneratorFromJSON(burst.count),", + " probability: burst.probability,", + " interval: burst.interval,", + " cycle: burst.cycle,", + " })),", + " onlyUsedByOther: json.onlyUsedByOther,", + " instancingGeometry: meta.geometries[json.instancingGeometry],", + " renderMode: json.renderMode,", + " rendererEmitterSettings: rendererEmitterSettings,", + " renderOrder: json.renderOrder,", + " layers: layers,", + " material: json.material", + " ? meta.materials[json.material]", + " : json.texture", + " ? new THREE.MeshBasicMaterial({", + " map: meta.textures[json.texture],", + " transparent: (_b = json.transparent) !== null && _b !== void 0 ? _b : true,", + " blending: json.blending,", + " side: THREE.DoubleSide,", + " })", + " : new THREE.MeshBasicMaterial({", + " color: 0xffffff,", + " transparent: true,", + " blending: THREE.AdditiveBlending,", + " side: THREE.DoubleSide,", + " }),", + " startTileIndex: typeof json.startTileIndex === 'number'", + " ? new ConstantValue(json.startTileIndex)", + " : ValueGeneratorFromJSON(json.startTileIndex),", + " uTileCount: json.uTileCount,", + " vTileCount: json.vTileCount,", + " behaviors: [],", + " worldSpace: json.worldSpace,", + " });", + " ps.behaviors = json.behaviors.map((behaviorJson) => {", + " const behavior = BehaviorFromJSON(behaviorJson, ps);", + " if (behavior.type === 'EmitSubParticleSystem') {", + " dependencies[behaviorJson.subParticleSystem] = behavior;", + " }", + " return behavior;", + " });", + " return ps;", + " }", + " addBehavior(behavior) {", + " this.behaviors.push(behavior);", + " }", + " getRendererSettings() {", + " return this.rendererSettings;", + " }", + " clone() {", + " const newEmissionBursts = [];", + " for (const emissionBurst of this.emissionBursts) {", + " const newEmissionBurst = {};", + " Object.assign(newEmissionBurst, emissionBurst);", + " newEmissionBursts.push(newEmissionBurst);", + " }", + " const newBehaviors = [];", + " for (const behavior of this.behaviors) {", + " newBehaviors.push(behavior.clone());", + " }", + " let rendererEmitterSettings;", + " if (this.renderMode === RenderMode.Trail) {", + " rendererEmitterSettings = {", + " startLength: this.rendererEmitterSettings.startLength.clone(),", + " followLocalOrigin: this.rendererEmitterSettings.followLocalOrigin,", + " };", + " }", + " else {", + " rendererEmitterSettings = {};", + " }", + " const layers = new THREE.Layers();", + " layers.mask = this.layers.mask;", + " return new ParticleSystem({", + " autoDestroy: this.autoDestroy,", + " looping: this.looping,", + " duration: this.duration,", + " shape: this.emitterShape.clone(),", + " startLife: this.startLife.clone(),", + " startSpeed: this.startSpeed.clone(),", + " startRotation: this.startRotation.clone(),", + " startSize: this.startSize.clone(),", + " startColor: this.startColor.clone(),", + " emissionOverTime: this.emissionOverTime.clone(),", + " emissionOverDistance: this.emissionOverDistance.clone(),", + " emissionBursts: newEmissionBursts,", + " onlyUsedByOther: this.onlyUsedByOther,", + " instancingGeometry: this.rendererSettings.instancingGeometry,", + " renderMode: this.renderMode,", + " renderOrder: this.renderOrder,", + " rendererEmitterSettings: rendererEmitterSettings,", + " material: this.rendererSettings.material,", + " startTileIndex: this.startTileIndex,", + " uTileCount: this.uTileCount,", + " vTileCount: this.vTileCount,", + " behaviors: newBehaviors,", + " worldSpace: this.worldSpace,", + " layers: layers,", + " });", + " }", + "}", + "", + "var particle_frag = `", + "", + "#include ", + "#include ", + "#include ", + "#include ", + "#include ", + "#include ", + "#include ", + "", + "void main() {", + "", + " #include ", + " ", + " vec3 outgoingLight = vec3( 0.0 );", + " vec4 diffuseColor = vColor;", + " ", + " #include ", + " ", + " #ifdef USE_MAP", + " diffuseColor *= texture2D( map, vMapUv);", + " #endif", + " ", + " #include ", + "", + " outgoingLight = diffuseColor.rgb;", + " ", + " #ifdef USE_COLOR_AS_ALPHA", + " gl_FragColor = vec4( outgoingLight, diffuseColor.r );", + " #else", + " gl_FragColor = vec4( outgoingLight, diffuseColor.a );", + " #endif", + " ", + " ", + " #include ", + "", + "}", + "`;", + "", + "var particle_physics_frag = `", + "#define STANDARD", + "#ifdef PHYSICAL", + "#define IOR", + "#define SPECULAR", + "#endif", + "uniform vec3 diffuse;", + "uniform vec3 emissive;", + "uniform float roughness;", + "uniform float metalness;", + "uniform float opacity;", + "#ifdef IOR", + "uniform float ior;", + "#endif", + "#ifdef SPECULAR", + "uniform float specularIntensity;", + "uniform vec3 specularColor;", + "#ifdef USE_SPECULARINTENSITYMAP", + "uniform sampler2D specularIntensityMap;", + "#endif", + "#ifdef USE_SPECULARCOLORMAP", + "uniform sampler2D specularColorMap;", + "#endif", + "#endif", + "#ifdef USE_CLEARCOAT", + "uniform float clearcoat;", + "uniform float clearcoatRoughness;", + "#endif", + "#ifdef USE_IRIDESCENCE", + "uniform float iridescence;", + "uniform float iridescenceIOR;", + "uniform float iridescenceThicknessMinimum;", + "uniform float iridescenceThicknessMaximum;", + "#endif", + "#ifdef USE_SHEEN", + "uniform vec3 sheenColor;", + "uniform float sheenRoughness;", + "#ifdef USE_SHEENCOLORMAP", + "uniform sampler2D sheenColorMap;", + "#endif", + "#ifdef USE_SHEENROUGHNESSMAP", + "uniform sampler2D sheenRoughnessMap;", + "#endif", + "#endif", + "", + "varying vec3 vViewPosition;", + "#include ", + "#include ", + "#include ", + "#include ", + "#include ", + "#include ", + "#include ", + "#include ", + "#include ", + "#include ", + "#include ", + "#include ", + "#include ", + "#include ", + "#include ", + "#include ", + "#include ", + "#include ", + "#include ", + "#include ", + "#include ", + "#include ", + "#include ", + "#include ", + "#include ", + "#include ", + "#include ", + "#include ", + "#include ", + "#include ", + "", + "void main() {", + " #include ", + " vec4 diffuseColor = vec4( diffuse, opacity );", + " ReflectedLight reflectedLight = ReflectedLight( vec3( 0.0 ), vec3( 0.0 ), vec3( 0.0 ), vec3( 0.0 ) );", + " vec3 totalEmissiveRadiance = emissive;", + " #include ", + " #include ", + " #include ", + " #include ", + " #include ", + " #include ", + " #include ", + " #include ", + " #include ", + " #include ", + " #include ", + " #include ", + " // accumulation", + " #include ", + " #include ", + " #include ", + " #include ", + " // modulation", + " #include ", + " vec3 totalDiffuse = reflectedLight.directDiffuse + reflectedLight.indirectDiffuse;", + " vec3 totalSpecular = reflectedLight.directSpecular + reflectedLight.indirectSpecular;", + " #include ", + " vec3 outgoingLight = totalDiffuse + totalSpecular + totalEmissiveRadiance;", + " #ifdef USE_SHEEN", + " // Sheen energy compensation approximation calculation can be found at the end of", + " // https://drive.google.com/file/d/1T0D1VSyR4AllqIJTQAraEIzjlb5h4FKH/view?usp=sharing", + " float sheenEnergyComp = 1.0 - 0.157 * max3( material.sheenColor );", + " outgoingLight = outgoingLight * sheenEnergyComp + sheenSpecular;", + " #endif", + " #ifdef USE_CLEARCOAT", + " float dotNVcc = saturate( dot( geometry.clearcoatNormal, geometry.viewDir ) );", + " vec3 Fcc = F_Schlick( material.clearcoatF0, material.clearcoatF90, dotNVcc );", + " outgoingLight = outgoingLight * ( 1.0 - material.clearcoat * Fcc ) + clearcoatSpecular * material.clearcoat;", + " #endif", + " #include ", + " #include ", + " #include ", + " #include ", + " #include ", + " #include ", + "}`;", + "", + "var uv_vertex_tile = `", + "", + " #ifdef UV_TILE", + " float col = mod(uvTile, tileCount.x);", + " float row = (tileCount.y - floor(uvTile / tileCount.x) - 1.0);", + " ", + " mat3 tileTransform = mat3(", + " 1.0 / tileCount.x, 0.0, 0.0,", + " 0.0, 1.0 / tileCount.y, 0.0, ", + " col / tileCount.x, row / tileCount.y, 1.0);", + " #else", + " mat3 tileTransform = mat3(1.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 1.0);", + " #endif", + "", + "#if defined( USE_UV ) || defined( USE_ANISOTROPY )", + "", + "vUv = (tileTransform *vec3( uv, 1 )).xy;", + "", + "#endif", + "#ifdef USE_MAP", + "", + "vMapUv = ( tileTransform * (mapTransform * vec3( MAP_UV, 1 ) )).xy;", + "", + "#endif", + "#ifdef USE_ALPHAMAP", + "", + "vAlphaMapUv = ( tileTransform * (alphaMapTransform * vec3( ALPHAMAP_UV, 1 ) )).xy;", + "", + "#endif", + "#ifdef USE_LIGHTMAP", + "", + "vLightMapUv = ( tileTransform * (lightMapTransform * vec3( LIGHTMAP_UV, 1 ) )).xy;", + "", + "#endif", + "#ifdef USE_AOMAP", + "", + "vAoMapUv = ( tileTransform * (aoMapTransform * vec3( AOMAP_UV, 1 ) )).xy;", + "", + "#endif", + "#ifdef USE_BUMPMAP", + "", + "vBumpMapUv = ( tileTransform * (bumpMapTransform * vec3( BUMPMAP_UV, 1 ) )).xy;", + "", + "#endif", + "#ifdef USE_NORMALMAP", + "", + "vNormalMapUv = ( tileTransform * (normalMapTransform * vec3( NORMALMAP_UV, 1 ) )).xy;", + "", + "#endif", + "#ifdef USE_DISPLACEMENTMAP", + "", + "vDisplacementMapUv = ( tileTransform * (displacementMapTransform * vec3( DISPLACEMENTMAP_UV, 1 ) )).xy;", + "", + "#endif", + "#ifdef USE_EMISSIVEMAP", + "", + "vEmissiveMapUv = ( tileTransform * (emissiveMapTransform * vec3( EMISSIVEMAP_UV, 1 ) )).xy;", + "", + "#endif", + "#ifdef USE_METALNESSMAP", + "", + "vMetalnessMapUv = ( tileTransform * (metalnessMapTransform * vec3( METALNESSMAP_UV, 1 ) )).xy;", + "", + "#endif", + "#ifdef USE_ROUGHNESSMAP", + "", + "vRoughnessMapUv = ( tileTransform * (roughnessMapTransform * vec3( ROUGHNESSMAP_UV, 1 ) )).xy;", + "", + "#endif", + "#ifdef USE_ANISOTROPYMAP", + "", + "vAnisotropyMapUv = ( tileTransform * (anisotropyMapTransform * vec3( ANISOTROPYMAP_UV, 1 ) )).xy;", + "", + "#endif", + "#ifdef USE_CLEARCOATMAP", + "", + "vClearcoatMapUv = ( tileTransform * (clearcoatMapTransform * vec3( CLEARCOATMAP_UV, 1 ) )).xy;", + "", + "#endif", + "#ifdef USE_CLEARCOAT_NORMALMAP", + "", + "vClearcoatNormalMapUv = ( tileTransform * (clearcoatNormalMapTransform * vec3( CLEARCOAT_NORMALMAP_UV, 1 ) )).xy;", + "", + "#endif", + "#ifdef USE_CLEARCOAT_ROUGHNESSMAP", + "", + "vClearcoatRoughnessMapUv = ( tileTransform * (clearcoatRoughnessMapTransform * vec3( CLEARCOAT_ROUGHNESSMAP_UV, 1 ) )).xy;", + "", + "#endif", + "#ifdef USE_IRIDESCENCEMAP", + "", + "vIridescenceMapUv = ( tileTransform * (iridescenceMapTransform * vec3( IRIDESCENCEMAP_UV, 1 ) )).xy;", + "", + "#endif", + "#ifdef USE_IRIDESCENCE_THICKNESSMAP", + "", + "vIridescenceThicknessMapUv = ( tileTransform * (iridescenceThicknessMapTransform * vec3( IRIDESCENCE_THICKNESSMAP_UV, 1 ) )).xy;", + "", + "#endif", + "#ifdef USE_SHEEN_COLORMAP", + "", + "vSheenColorMapUv = ( tileTransform * (sheenColorMapTransform * vec3( SHEEN_COLORMAP_UV, 1 ) )).xy;", + "", + "#endif", + "#ifdef USE_SHEEN_ROUGHNESSMAP", + "", + "vSheenRoughnessMapUv = ( tileTransform * (sheenRoughnessMapTransform * vec3( SHEEN_ROUGHNESSMAP_UV, 1 ) )).xy;", + "", + "#endif", + "#ifdef USE_SPECULARMAP", + "", + "vSpecularMapUv = ( tileTransform * (specularMapTransform * vec3( SPECULARMAP_UV, 1 ) )).xy;", + "", + "#endif", + "#ifdef USE_SPECULAR_COLORMAP", + "", + "vSpecularColorMapUv = ( tileTransform * (specularColorMapTransform * vec3( SPECULAR_COLORMAP_UV, 1 ) )).xy;", + "", + "#endif", + "#ifdef USE_SPECULAR_INTENSITYMAP", + "", + "vSpecularIntensityMapUv = ( tileTransform * (specularIntensityMapTransform * vec3( SPECULAR_INTENSITYMAP_UV, 1 ) )).xy;", + "", + "#endif", + "#ifdef USE_TRANSMISSIONMAP", + "", + "vTransmissionMapUv = ( tileTransform * transmissionMapTransform * vec3( TRANSMISSIONMAP_UV, 1 ) )).xy;", + "", + "#endif", + "#ifdef USE_THICKNESSMAP", + "", + "vThicknessMapUv = ( tileTransform * thicknessMapTransform * vec3( THICKNESSMAP_UV, 1 ) )).xy;", + "", + "#endif", + "`;", + "", + "var particle_vert = `", + "#include ", + "#include ", + "#include ", + "#include ", + "#include ", + "", + "attribute vec3 offset;", + "attribute float rotation;", + "attribute float size;", + "attribute float uvTile;", + "", + "#ifdef UV_TILE", + "uniform vec2 tileCount;", + "#endif", + "", + "void main() {", + "", + " ${uv_vertex_tile}", + "\t", + " vec2 alignedPosition = ( position.xy ) * size;", + " ", + " vec2 rotatedPosition;", + " rotatedPosition.x = cos( rotation ) * alignedPosition.x - sin( rotation ) * alignedPosition.y;", + " rotatedPosition.y = sin( rotation ) * alignedPosition.x + cos( rotation ) * alignedPosition.y;", + "#ifdef HORIZONTAL", + " vec4 mvPosition = modelMatrix * vec4( offset, 1.0 );", + " mvPosition.x += rotatedPosition.x;", + " mvPosition.z -= rotatedPosition.y;", + " mvPosition = viewMatrix * mvPosition;", + "#elif defined(VERTICAL)", + " vec4 mvPosition = modelMatrix * vec4( offset, 1.0 );", + " mvPosition.y += rotatedPosition.y;", + " mvPosition = viewMatrix * mvPosition;", + " mvPosition.x += rotatedPosition.x;", + "#else", + " vec4 mvPosition = modelViewMatrix * vec4( offset, 1.0 );", + " mvPosition.xy += rotatedPosition;", + "#endif", + "", + "\tvColor = color;", + "", + "\tgl_Position = projectionMatrix * mvPosition;", + "", + "\t#include ", + "\t#include ", + "", + "}", + "`;", + "", + "var local_particle_vert = `", + "#include ", + "#include ", + "#include ", + "#include ", + "#include ", + "", + "attribute vec3 offset;", + "attribute vec4 rotation;", + "attribute float size;", + "// attribute vec4 color;", + "attribute float uvTile;", + "", + "#ifdef UV_TILE", + "uniform vec2 tileCount;", + "#endif", + "", + "void main() {", + "", + " ${uv_vertex_tile}", + " ", + " float x2 = rotation.x + rotation.x, y2 = rotation.y + rotation.y, z2 = rotation.z + rotation.z;", + " float xx = rotation.x * x2, xy = rotation.x * y2, xz = rotation.x * z2;", + " float yy = rotation.y * y2, yz = rotation.y * z2, zz = rotation.z * z2;", + " float wx = rotation.w * x2, wy = rotation.w * y2, wz = rotation.w * z2;", + " float sx = size, sy = size, sz = size;", + " ", + " mat4 matrix = mat4(( 1.0 - ( yy + zz ) ) * sx, ( xy + wz ) * sx, ( xz - wy ) * sx, 0.0, // 1. column", + " ( xy - wz ) * sy, ( 1.0 - ( xx + zz ) ) * sy, ( yz + wx ) * sy, 0.0, // 2. column", + " ( xz + wy ) * sz, ( yz - wx ) * sz, ( 1.0 - ( xx + yy ) ) * sz, 0.0, // 3. column", + " offset.x, offset.y, offset.z, 1.0);", + " ", + " vec4 mvPosition = modelViewMatrix * (matrix * vec4( position, 1.0 ));", + "", + "\tvColor = color;", + "", + "\tgl_Position = projectionMatrix * mvPosition;", + "", + "\t#include ", + "\t#include ", + "", + "}", + "`;", + "", + "var local_particle_physics_vert = `", + "#define STANDARD", + "varying vec3 vViewPosition;", + "#ifdef USE_TRANSMISSION", + "\tvarying vec3 vWorldPosition;", + "#endif", + "#include ", + "#include ", + "", + "attribute vec3 offset;", + "attribute vec4 rotation;", + "attribute float size;", + "attribute float uvTile;", + "", + "#ifdef UV_TILE", + "uniform vec2 tileCount;", + "#endif", + "", + "#include ", + "#include ", + "#include ", + "#include ", + "#include ", + "#include ", + "#include ", + "#include ", + "#include ", + "", + "void main() {", + " ${uv_vertex_tile}", + "", + " float x2 = rotation.x + rotation.x, y2 = rotation.y + rotation.y, z2 = rotation.z + rotation.z;", + " float xx = rotation.x * x2, xy = rotation.x * y2, xz = rotation.x * z2;", + " float yy = rotation.y * y2, yz = rotation.y * z2, zz = rotation.z * z2;", + " float wx = rotation.w * x2, wy = rotation.w * y2, wz = rotation.w * z2;", + " float sx = size, sy = size, sz = size;", + "", + " mat4 particleMatrix = mat4(( 1.0 - ( yy + zz ) ) * sx, ( xy + wz ) * sx, ( xz - wy ) * sx, 0.0, // 1. column", + " ( xy - wz ) * sy, ( 1.0 - ( xx + zz ) ) * sy, ( yz + wx ) * sy, 0.0, // 2. column", + " ( xz + wy ) * sz, ( yz - wx ) * sz, ( 1.0 - ( xx + yy ) ) * sz, 0.0, // 3. column", + " offset.x, offset.y, offset.z, 1.0);", + "", + "\t#include ", + "\t#include ", + "\t#include ", + "\t#include ", + "\t#include ", + "\t#include ", + "", + "\t// replace defaultnormal_vertex", + "\tvec3 transformedNormal = objectNormal;", + " mat3 m = mat3( particleMatrix );", + " transformedNormal /= vec3( dot( m[ 0 ], m[ 0 ] ), dot( m[ 1 ], m[ 1 ] ), dot( m[ 2 ], m[ 2 ] ) );", + " transformedNormal = m * transformedNormal;", + " transformedNormal = normalMatrix * transformedNormal;", + " #ifdef FLIP_SIDED", + " transformedNormal = - transformedNormal;", + " #endif", + " #ifdef USE_TANGENT", + " vec3 transformedTangent = ( modelViewMatrix * vec4( objectTangent, 0.0 ) ).xyz;", + " #ifdef FLIP_SIDED", + " transformedTangent = - transformedTangent;", + " #endif", + " #endif", + "", + "\t#include ", + "\t#include ", + "\t#include ", + "\t#include ", + "\t#include ", + "", + "\t// replace include ", + " vec4 mvPosition = vec4( transformed, 1.0 );", + " mvPosition = modelViewMatrix * (particleMatrix * mvPosition);", + "\tgl_Position = projectionMatrix * mvPosition;", + "", + "\t#include ", + "\t#include ", + "\t", + "\tvViewPosition = - mvPosition.xyz;", + "\t", + "\t#include ", + "\t#include ", + "\t#include ", + "#ifdef USE_TRANSMISSION", + "\tvWorldPosition = worldPosition.xyz;", + "#endif", + "}", + "`;", + "", + "var stretched_bb_particle_vert = `", + "#include ", + "#include ", + "#include ", + "#include ", + "#include ", + "", + "attribute vec3 offset;", + "attribute float rotation;", + "attribute float size;", + "attribute vec4 velocity;", + "attribute float uvTile;", + "", + "#ifdef UV_TILE", + "uniform vec2 tileCount;", + "#endif", + "", + "uniform float speedFactor;", + "", + "void main() {", + "", + " ${uv_vertex_tile}", + " ", + " float lengthFactor = velocity.w;", + "#ifdef USE_SKEW", + " vec4 mvPosition = modelViewMatrix * vec4( offset, 1.0 );", + " vec3 viewVelocity = normalMatrix * velocity.xyz;", + "", + " vec3 scaledPos = vec3(position.xy * size, position.z);", + " float vlength = length(viewVelocity);", + " vec3 projVelocity = dot(scaledPos, viewVelocity) * viewVelocity / vlength;", + " mvPosition.xyz += scaledPos + projVelocity * (speedFactor / size + lengthFactor / vlength);", + "#else", + " vec4 mvPosition = modelViewMatrix * vec4( offset, 1.0 );", + " vec3 viewVelocity = normalMatrix * velocity.xyz;", + " float vlength = length(viewVelocity); ", + " mvPosition.xyz += position.y * normalize(cross(mvPosition.xyz, viewVelocity)) * size; // switch the cross to match unity implementation", + " mvPosition.xyz -= (position.x + 0.5) * viewVelocity * (1.0 + lengthFactor / vlength) * size; // minus position.x to match unity implementation", + "#endif", + "\tvColor = color;", + "\tgl_Position = projectionMatrix * mvPosition;", + "\t#include ", + "\t#include ", + "}", + "`;", + "", + "function getMaterialUVChannelName(value) {", + " if (value === 0)", + " return 'uv';", + " return `uv${value}`;", + "}", + "", + "new THREE.Vector3(0, 0, 1);", + "class SpriteBatch extends VFXBatch {", + " constructor(settings) {", + " super(settings);", + " this.vector_ = new THREE.Vector3();", + " this.vector2_ = new THREE.Vector3();", + " this.vector3_ = new THREE.Vector3();", + " this.quaternion_ = new THREE.Quaternion();", + " this.quaternion2_ = new THREE.Quaternion();", + " this.quaternion3_ = new THREE.Quaternion();", + " this.rotationMat_ = new THREE.Matrix3();", + " this.rotationMat2_ = new THREE.Matrix3();", + " this.maxParticles = 1000;", + " this.setupBuffers();", + " this.rebuildMaterial();", + " }", + " buildExpandableBuffers() {", + " this.offsetBuffer = new THREE.InstancedBufferAttribute(new Float32Array(this.maxParticles * 3), 3);", + " this.offsetBuffer.setUsage(THREE.DynamicDrawUsage);", + " this.geometry.setAttribute('offset', this.offsetBuffer);", + " this.colorBuffer = new THREE.InstancedBufferAttribute(new Float32Array(this.maxParticles * 4), 4);", + " this.colorBuffer.setUsage(THREE.DynamicDrawUsage);", + " this.geometry.setAttribute('color', this.colorBuffer);", + " if (this.settings.renderMode === RenderMode.Mesh) {", + " this.rotationBuffer = new THREE.InstancedBufferAttribute(new Float32Array(this.maxParticles * 4), 4);", + " this.rotationBuffer.setUsage(THREE.DynamicDrawUsage);", + " this.geometry.setAttribute('rotation', this.rotationBuffer);", + " }", + " else if (this.settings.renderMode === RenderMode.BillBoard ||", + " this.settings.renderMode === RenderMode.HorizontalBillBoard ||", + " this.settings.renderMode === RenderMode.VerticalBillBoard ||", + " this.settings.renderMode === RenderMode.StretchedBillBoard) {", + " this.rotationBuffer = new THREE.InstancedBufferAttribute(new Float32Array(this.maxParticles), 1);", + " this.rotationBuffer.setUsage(THREE.DynamicDrawUsage);", + " this.geometry.setAttribute('rotation', this.rotationBuffer);", + " }", + " this.sizeBuffer = new THREE.InstancedBufferAttribute(new Float32Array(this.maxParticles), 1);", + " this.sizeBuffer.setUsage(THREE.DynamicDrawUsage);", + " this.geometry.setAttribute('size', this.sizeBuffer);", + " this.uvTileBuffer = new THREE.InstancedBufferAttribute(new Float32Array(this.maxParticles), 1);", + " this.uvTileBuffer.setUsage(THREE.DynamicDrawUsage);", + " this.geometry.setAttribute('uvTile', this.uvTileBuffer);", + " if (this.settings.renderMode === RenderMode.StretchedBillBoard) {", + " this.velocityBuffer = new THREE.InstancedBufferAttribute(new Float32Array(this.maxParticles * 4), 4);", + " this.velocityBuffer.setUsage(THREE.DynamicDrawUsage);", + " this.geometry.setAttribute('velocity', this.velocityBuffer);", + " }", + " }", + " setupBuffers() {", + " if (this.geometry)", + " this.geometry.dispose();", + " this.geometry = new THREE.InstancedBufferGeometry();", + " this.geometry.setIndex(this.settings.instancingGeometry.getIndex());", + " if (this.settings.instancingGeometry.hasAttribute('normal')) {", + " this.geometry.setAttribute('normal', this.settings.instancingGeometry.getAttribute('normal'));", + " }", + " this.geometry.setAttribute('position', this.settings.instancingGeometry.getAttribute('position'));", + " this.geometry.setAttribute('uv', this.settings.instancingGeometry.getAttribute('uv'));", + " this.buildExpandableBuffers();", + " }", + " expandBuffers(target) {", + " while (target >= this.maxParticles) {", + " this.maxParticles *= 2;", + " }", + " this.setupBuffers();", + " }", + " rebuildMaterial() {", + " this.layers.mask = this.settings.layers.mask;", + " let uniforms;", + " const defines = {};", + " if (this.settings.material.type === 'MeshStandardMaterial' ||", + " this.settings.material.type === 'MeshPhysicalMaterial') {", + " const mat = this.settings.material;", + " uniforms = THREE.UniformsUtils.merge([", + " THREE.UniformsLib.common,", + " THREE.UniformsLib.envmap,", + " THREE.UniformsLib.aomap,", + " THREE.UniformsLib.lightmap,", + " THREE.UniformsLib.emissivemap,", + " THREE.UniformsLib.bumpmap,", + " THREE.UniformsLib.normalmap,", + " THREE.UniformsLib.displacementmap,", + " THREE.UniformsLib.roughnessmap,", + " THREE.UniformsLib.metalnessmap,", + " THREE.UniformsLib.fog,", + " THREE.UniformsLib.lights,", + " {", + " emissive: { value: new THREE.Color(0x000000) },", + " roughness: { value: 1.0 },", + " metalness: { value: 0.0 },", + " envMapIntensity: { value: 1 },", + " },", + " ]);", + " uniforms['diffuse'].value = mat.color;", + " uniforms['opacity'].value = mat.opacity;", + " uniforms['emissive'].value = mat.emissive;", + " uniforms['roughness'].value = mat.roughness;", + " uniforms['metalness'].value = mat.metalness;", + " if (mat.envMap) {", + " uniforms['envMap'].value = mat.envMap;", + " uniforms['envMapIntensity'].value = mat.envMapIntensity;", + " }", + " if (mat.normalMap) {", + " uniforms['normalMap'].value = mat.normalMap;", + " uniforms['normalScale'].value = mat.normalScale;", + " }", + " if (mat.roughnessMap) {", + " uniforms['roughnessMap'].value = mat.roughnessMap;", + " }", + " if (mat.metalnessMap) {", + " uniforms['metalnessMap'].value = mat.metalnessMap;", + " }", + " if (mat.map) {", + " uniforms['map'] = new THREE.Uniform(mat.map);", + " }", + " }", + " else {", + " uniforms = {};", + " uniforms['map'] = new THREE.Uniform(this.settings.material.map);", + " }", + " if (this.settings.material.alphaTest) {", + " defines['USE_ALPHATEST'] = '';", + " uniforms['alphaTest'] = new THREE.Uniform(this.settings.material.alphaTest);", + " }", + " defines['USE_UV'] = '';", + " const uTileCount = this.settings.uTileCount;", + " const vTileCount = this.settings.vTileCount;", + " if (uTileCount > 1 || vTileCount > 1) {", + " defines['UV_TILE'] = '';", + " uniforms['tileCount'] = new THREE.Uniform(new THREE.Vector2(uTileCount, vTileCount));", + " }", + " if (this.settings.material.defines &&", + " this.settings.material.defines['USE_COLOR_AS_ALPHA'] !== undefined) {", + " defines['USE_COLOR_AS_ALPHA'] = '';", + " }", + " if (this.settings.material.normalMap) {", + " defines['USE_NORMALMAP'] = '';", + " defines['NORMALMAP_UV'] = getMaterialUVChannelName(this.settings.material.normalMap.channel);", + " uniforms['normalMapTransform'] = new THREE.Uniform(new THREE.Matrix3().copy(this.settings.material.normalMap.matrix));", + " }", + " if (this.settings.material.map) {", + " defines['USE_MAP'] = '';", + " defines['MAP_UV'] = getMaterialUVChannelName(this.settings.material.map.channel);", + " uniforms['mapTransform'] = new THREE.Uniform(new THREE.Matrix3().copy(this.settings.material.map.matrix));", + " }", + " defines['USE_COLOR_ALPHA'] = '';", + " let needLights = false;", + " if (this.settings.renderMode === RenderMode.BillBoard ||", + " this.settings.renderMode === RenderMode.VerticalBillBoard ||", + " this.settings.renderMode === RenderMode.HorizontalBillBoard ||", + " this.settings.renderMode === RenderMode.Mesh) {", + " let vertexShader;", + " let fragmentShader;", + " if (this.settings.renderMode === RenderMode.Mesh) {", + " if (this.settings.material.type === 'MeshStandardMaterial' ||", + " this.settings.material.type === 'MeshPhysicalMaterial') {", + " defines['USE_COLOR'] = '';", + " vertexShader = local_particle_physics_vert;", + " fragmentShader = particle_physics_frag;", + " needLights = true;", + " }", + " else {", + " vertexShader = local_particle_vert;", + " fragmentShader = particle_frag;", + " }", + " }", + " else {", + " vertexShader = particle_vert;", + " fragmentShader = particle_frag;", + " }", + " if (this.settings.renderMode === RenderMode.VerticalBillBoard) {", + " defines['VERTICAL'] = '';", + " }", + " else if (this.settings.renderMode === RenderMode.HorizontalBillBoard) {", + " defines['HORIZONTAL'] = '';", + " }", + " this.material = new THREE.ShaderMaterial({", + " uniforms: uniforms,", + " defines: defines,", + " vertexShader: vertexShader,", + " fragmentShader: fragmentShader,", + " transparent: this.settings.material.transparent,", + " depthWrite: !this.settings.material.transparent,", + " blending: this.settings.material.blending,", + " side: this.settings.material.side,", + " alphaTest: this.settings.material.alphaTest,", + " lights: needLights,", + " });", + " }", + " else if (this.settings.renderMode === RenderMode.StretchedBillBoard) {", + " uniforms['speedFactor'] = new THREE.Uniform(1.0);", + " this.material = new THREE.ShaderMaterial({", + " uniforms: uniforms,", + " defines: defines,", + " vertexShader: stretched_bb_particle_vert,", + " fragmentShader: particle_frag,", + " transparent: this.settings.material.transparent,", + " depthWrite: !this.settings.material.transparent,", + " blending: this.settings.material.blending,", + " side: this.settings.material.side,", + " alphaTest: this.settings.material.alphaTest,", + " });", + " }", + " else {", + " throw new Error('render mode unavailable');", + " }", + " }", + " update() {", + " let index = 0;", + " let particleCount = 0;", + " this.systems.forEach((system) => {", + " particleCount += system.particleNum;", + " });", + " if (particleCount > this.maxParticles) {", + " this.expandBuffers(particleCount);", + " }", + " this.systems.forEach((system) => {", + " const particles = system.particles;", + " const particleNum = system.particleNum;", + " const rotation = this.quaternion2_;", + " const translation = this.vector2_;", + " const scale = this.vector3_;", + " system.emitter.matrixWorld.decompose(translation, rotation, scale);", + " this.rotationMat_.setFromMatrix4(system.emitter.matrixWorld);", + " for (let j = 0; j < particleNum; j++ , index++) {", + " const particle = particles[j];", + " if (this.settings.renderMode === RenderMode.Mesh) {", + " let q;", + " if (system.worldSpace) {", + " q = particle.rotation;", + " }", + " else {", + " let parentQ;", + " if (particle.parentMatrix) {", + " parentQ = this.quaternion3_.setFromRotationMatrix(particle.parentMatrix);", + " }", + " else {", + " parentQ = rotation;", + " }", + " q = this.quaternion_;", + " q.copy(particle.rotation).multiply(parentQ);", + " }", + " this.rotationBuffer.setXYZW(index, q.x, q.y, q.z, q.w);", + " }", + " else if (this.settings.renderMode === RenderMode.StretchedBillBoard ||", + " this.settings.renderMode === RenderMode.VerticalBillBoard ||", + " this.settings.renderMode === RenderMode.HorizontalBillBoard ||", + " this.settings.renderMode === RenderMode.BillBoard) {", + " this.rotationBuffer.setX(index, particle.rotation);", + " }", + " let vec;", + " if (system.worldSpace) {", + " vec = particle.position;", + " }", + " else {", + " vec = this.vector_;", + " if (particle.parentMatrix) {", + " vec.copy(particle.position).applyMatrix4(particle.parentMatrix);", + " }", + " else {", + " vec.copy(particle.position).applyMatrix4(system.emitter.matrixWorld);", + " }", + " }", + " this.offsetBuffer.setXYZ(index, vec.x, vec.y, vec.z);", + " this.colorBuffer.setXYZW(index, particle.color.x, particle.color.y, particle.color.z, particle.color.w);", + " if (system.worldSpace) {", + " this.sizeBuffer.setX(index, particle.size);", + " }", + " else {", + " if (particle.parentMatrix) {", + " this.sizeBuffer.setX(index, particle.size);", + " }", + " else {", + " this.sizeBuffer.setX(index, (particle.size * (Math.abs(scale.x) + Math.abs(scale.y) + Math.abs(scale.z))) / 3);", + " }", + " }", + " this.uvTileBuffer.setX(index, particle.uvTile);", + " if (this.settings.renderMode === RenderMode.StretchedBillBoard && this.velocityBuffer) {", + " let speedFactor = system.rendererEmitterSettings.speedFactor;", + " if (speedFactor === 0)", + " speedFactor = 0.001;", + " const lengthFactor = system.rendererEmitterSettings.lengthFactor;", + " let vec;", + " if (system.worldSpace) {", + " vec = particle.velocity;", + " }", + " else {", + " vec = this.vector_;", + " if (particle.parentMatrix) {", + " this.rotationMat2_.setFromMatrix4(particle.parentMatrix);", + " vec.copy(particle.velocity).applyMatrix3(this.rotationMat2_);", + " }", + " else {", + " vec.copy(particle.velocity).applyMatrix3(this.rotationMat_);", + " }", + " }", + " this.velocityBuffer.setXYZW(index, vec.x * speedFactor, vec.y * speedFactor, vec.z * speedFactor, lengthFactor);", + " }", + " }", + " });", + " this.geometry.instanceCount = index;", + " if (index > 0) {", + " this.offsetBuffer.clearUpdateRanges();", + " this.offsetBuffer.addUpdateRange(0, index * 3);", + " this.offsetBuffer.needsUpdate = true;", + " this.sizeBuffer.clearUpdateRanges();", + " this.sizeBuffer.addUpdateRange(0, index);", + " this.sizeBuffer.needsUpdate = true;", + " this.colorBuffer.clearUpdateRanges();", + " this.colorBuffer.addUpdateRange(0, index * 4);", + " this.colorBuffer.needsUpdate = true;", + " this.uvTileBuffer.clearUpdateRanges();", + " this.uvTileBuffer.addUpdateRange(0, index);", + " this.uvTileBuffer.needsUpdate = true;", + " if (this.settings.renderMode === RenderMode.StretchedBillBoard && this.velocityBuffer) {", + " this.velocityBuffer.clearUpdateRanges();", + " this.velocityBuffer.addUpdateRange(0, index * 4);", + " this.velocityBuffer.needsUpdate = true;", + " }", + " if (this.settings.renderMode === RenderMode.Mesh) {", + " this.rotationBuffer.clearUpdateRanges();", + " this.rotationBuffer.addUpdateRange(0, index * 4);", + " this.rotationBuffer.needsUpdate = true;", + " }", + " else if (this.settings.renderMode === RenderMode.StretchedBillBoard ||", + " this.settings.renderMode === RenderMode.HorizontalBillBoard ||", + " this.settings.renderMode === RenderMode.VerticalBillBoard ||", + " this.settings.renderMode === RenderMode.BillBoard) {", + " this.rotationBuffer.clearUpdateRanges();", + " this.rotationBuffer.addUpdateRange(0, index);", + " this.rotationBuffer.needsUpdate = true;", + " }", + " }", + " }", + " dispose() {", + " this.geometry.dispose();", + " }", + "}", + "", + "var trail_frag = `", + "", + "#include ", + "#include ", + "#include ", + "#include ", + "#include ", + "#include ", + "", + "uniform sampler2D alphaMap;", + "uniform float useAlphaMap;", + "uniform float visibility;", + "uniform float alphaTest;", + "uniform vec2 repeat;", + "", + "varying vec4 vColor;", + " ", + "void main() {", + " #include ", + " #include ", + "", + " vec4 c = vColor;", + " ", + " #ifdef USE_MAP", + " #ifdef USE_COLOR_AS_ALPHA", + " vec4 tex = texture2D( map, vUv * repeat );", + " c *= vec4(tex.rgb, tex.r);", + " #else", + " c *= texture2D( map, vUv * repeat );", + " #endif", + " #endif", + " if( useAlphaMap == 1. ) c.a *= texture2D( alphaMap, vUv * repeat ).a;", + " if( c.a < alphaTest ) discard;", + " gl_FragColor = c;", + "", + " #include ", + " #include ", + "}`;", + "", + "var trail_vert = `", + "#include ", + "#include ", + "#include ", + "#include ", + "#include ", + "#include ", + "", + "attribute vec3 previous;", + "attribute vec3 next;", + "attribute float side;", + "attribute float width;", + "", + "uniform vec2 resolution;", + "uniform float lineWidth;", + "uniform float sizeAttenuation;", + " ", + "vec2 fix(vec4 i, float aspect) {", + " vec2 res = i.xy / i.w;", + " res.x *= aspect;", + " return res;", + "}", + " ", + "void main() {", + "", + " ${uv_vertex_tile}", + " ", + " float aspect = resolution.x / resolution.y;", + "", + " vColor = color;", + "", + " mat4 m = projectionMatrix * modelViewMatrix;", + " vec4 finalPosition = m * vec4( position, 1.0 );", + " vec4 prevPos = m * vec4( previous, 1.0 );", + " vec4 nextPos = m * vec4( next, 1.0 );", + "", + " vec2 currentP = fix( finalPosition, aspect );", + " vec2 prevP = fix( prevPos, aspect );", + " vec2 nextP = fix( nextPos, aspect );", + "", + " float w = lineWidth * width;", + "", + " vec2 dir;", + " if( nextP == currentP ) dir = normalize( currentP - prevP );", + " else if( prevP == currentP ) dir = normalize( nextP - currentP );", + " else {", + " vec2 dir1 = normalize( currentP - prevP );", + " vec2 dir2 = normalize( nextP - currentP );", + " dir = normalize( dir1 + dir2 );", + "", + " vec2 perp = vec2( -dir1.y, dir1.x );", + " vec2 miter = vec2( -dir.y, dir.x );", + " //w = clamp( w / dot( miter, perp ), 0., 4., * lineWidth * width );", + "", + " }", + "", + " //vec2 normal = ( cross( vec3( dir, 0. ) vec3( 0., 0., 1. ) ) ).xy;", + " vec4 normal = vec4( -dir.y, dir.x, 0., 1. );", + " normal.xy *= .5 * w;", + " normal *= projectionMatrix;", + " if( sizeAttenuation == 0. ) {", + " normal.xy *= finalPosition.w;", + " normal.xy /= ( vec4( resolution, 0., 1. ) * projectionMatrix ).xy;", + " }", + "", + " finalPosition.xy += normal.xy * side;", + "", + " gl_Position = finalPosition;", + "", + "\t#include ", + "\t#include ", + "\t", + " vec4 mvPosition = modelViewMatrix * vec4( position, 1.0 );", + " ", + "\t#include ", + "}`;", + "", + "new THREE.Vector3(0, 0, 1);", + "class TrailBatch extends VFXBatch {", + " constructor(settings) {", + " super(settings);", + " this.vector_ = new THREE.Vector3();", + " this.vector2_ = new THREE.Vector3();", + " this.vector3_ = new THREE.Vector3();", + " this.quaternion_ = new THREE.Quaternion();", + " this.maxParticles = 10000;", + " this.setupBuffers();", + " this.rebuildMaterial();", + " }", + " setupBuffers() {", + " if (this.geometry)", + " this.geometry.dispose();", + " this.geometry = new THREE.BufferGeometry();", + " this.indexBuffer = new THREE.BufferAttribute(new Uint32Array(this.maxParticles * 6), 1);", + " this.indexBuffer.setUsage(THREE.DynamicDrawUsage);", + " this.geometry.setIndex(this.indexBuffer);", + " this.positionBuffer = new THREE.BufferAttribute(new Float32Array(this.maxParticles * 6), 3);", + " this.positionBuffer.setUsage(THREE.DynamicDrawUsage);", + " this.geometry.setAttribute('position', this.positionBuffer);", + " this.previousBuffer = new THREE.BufferAttribute(new Float32Array(this.maxParticles * 6), 3);", + " this.previousBuffer.setUsage(THREE.DynamicDrawUsage);", + " this.geometry.setAttribute('previous', this.previousBuffer);", + " this.nextBuffer = new THREE.BufferAttribute(new Float32Array(this.maxParticles * 6), 3);", + " this.nextBuffer.setUsage(THREE.DynamicDrawUsage);", + " this.geometry.setAttribute('next', this.nextBuffer);", + " this.widthBuffer = new THREE.BufferAttribute(new Float32Array(this.maxParticles * 2), 1);", + " this.widthBuffer.setUsage(THREE.DynamicDrawUsage);", + " this.geometry.setAttribute('width', this.widthBuffer);", + " this.sideBuffer = new THREE.BufferAttribute(new Float32Array(this.maxParticles * 2), 1);", + " this.sideBuffer.setUsage(THREE.DynamicDrawUsage);", + " this.geometry.setAttribute('side', this.sideBuffer);", + " this.uvBuffer = new THREE.BufferAttribute(new Float32Array(this.maxParticles * 4), 2);", + " this.uvBuffer.setUsage(THREE.DynamicDrawUsage);", + " this.geometry.setAttribute('uv', this.uvBuffer);", + " this.colorBuffer = new THREE.BufferAttribute(new Float32Array(this.maxParticles * 8), 4);", + " this.colorBuffer.setUsage(THREE.DynamicDrawUsage);", + " this.geometry.setAttribute('color', this.colorBuffer);", + " }", + " expandBuffers(target) {", + " while (target >= this.maxParticles) {", + " this.maxParticles *= 2;", + " }", + " this.setupBuffers();", + " }", + " rebuildMaterial() {", + " this.layers.mask = this.settings.layers.mask;", + " const uniforms = {", + " lineWidth: { value: 1 },", + " map: { value: null },", + " useMap: { value: 0 },", + " alphaMap: { value: null },", + " useAlphaMap: { value: 0 },", + " resolution: { value: new THREE.Vector2(1, 1) },", + " sizeAttenuation: { value: 1 },", + " visibility: { value: 1 },", + " alphaTest: { value: 0 },", + " repeat: { value: new THREE.Vector2(1, 1) },", + " };", + " const defines = {};", + " defines['USE_UV'] = '';", + " defines['USE_COLOR_ALPHA'] = '';", + " if (this.settings.material.map) {", + " defines['USE_MAP'] = '';", + " defines['MAP_UV'] = getMaterialUVChannelName(this.settings.material.map.channel);", + " uniforms['map'] = new THREE.Uniform(this.settings.material.map);", + " uniforms['mapTransform'] = new THREE.Uniform(new THREE.Matrix3().copy(this.settings.material.map.matrix));", + " }", + " if (this.settings.material.defines &&", + " this.settings.material.defines['USE_COLOR_AS_ALPHA'] !== undefined) {", + " defines['USE_COLOR_AS_ALPHA'] = '';", + " }", + " if (this.settings.renderMode === RenderMode.Trail) {", + " this.material = new THREE.ShaderMaterial({", + " uniforms: uniforms,", + " defines: defines,", + " vertexShader: trail_vert,", + " fragmentShader: trail_frag,", + " transparent: this.settings.material.transparent,", + " depthWrite: !this.settings.material.transparent,", + " side: this.settings.material.side,", + " blending: this.settings.material.blending || THREE.AdditiveBlending,", + " });", + " }", + " else {", + " throw new Error('render mode unavailable');", + " }", + " }", + " update() {", + " let index = 0;", + " let triangles = 0;", + " let particleCount = 0;", + " this.systems.forEach((system) => {", + " for (let j = 0; j < system.particleNum; j++) {", + " particleCount += system.particles[j].previous.length * 2;", + " }", + " });", + " if (particleCount > this.maxParticles) {", + " this.expandBuffers(particleCount);", + " }", + " this.systems.forEach((system) => {", + " const rotation = this.quaternion_;", + " const translation = this.vector2_;", + " const scale = this.vector3_;", + " system.emitter.matrixWorld.decompose(translation, rotation, scale);", + " const particles = system.particles;", + " const particleNum = system.particleNum;", + " const uTileCount = this.settings.uTileCount;", + " const vTileCount = this.settings.vTileCount;", + " const tileWidth = 1 / uTileCount;", + " const tileHeight = 1 / vTileCount;", + " for (let j = 0; j < particleNum; j++) {", + " const particle = particles[j];", + " const col = particle.uvTile % vTileCount;", + " const row = Math.floor(particle.uvTile / vTileCount + 0.001);", + " const iter = particle.previous.values();", + " let curIter = iter.next();", + " let previous = curIter.value;", + " let current = previous;", + " if (!curIter.done)", + " curIter = iter.next();", + " let next;", + " if (curIter.value !== undefined) {", + " next = curIter.value;", + " }", + " else {", + " next = current;", + " }", + " for (let i = 0; i < particle.previous.length; i++ , index += 2) {", + " this.positionBuffer.setXYZ(index, current.position.x, current.position.y, current.position.z);", + " this.positionBuffer.setXYZ(index + 1, current.position.x, current.position.y, current.position.z);", + " if (system.worldSpace) {", + " this.positionBuffer.setXYZ(index, current.position.x, current.position.y, current.position.z);", + " this.positionBuffer.setXYZ(index + 1, current.position.x, current.position.y, current.position.z);", + " }", + " else {", + " if (particle.parentMatrix) {", + " this.vector_.copy(current.position).applyMatrix4(particle.parentMatrix);", + " }", + " else {", + " this.vector_.copy(current.position).applyMatrix4(system.emitter.matrixWorld);", + " }", + " this.positionBuffer.setXYZ(index, this.vector_.x, this.vector_.y, this.vector_.z);", + " this.positionBuffer.setXYZ(index + 1, this.vector_.x, this.vector_.y, this.vector_.z);", + " }", + " if (system.worldSpace) {", + " this.previousBuffer.setXYZ(index, previous.position.x, previous.position.y, previous.position.z);", + " this.previousBuffer.setXYZ(index + 1, previous.position.x, previous.position.y, previous.position.z);", + " }", + " else {", + " if (particle.parentMatrix) {", + " this.vector_.copy(previous.position).applyMatrix4(particle.parentMatrix);", + " }", + " else {", + " this.vector_.copy(previous.position).applyMatrix4(system.emitter.matrixWorld);", + " }", + " this.previousBuffer.setXYZ(index, this.vector_.x, this.vector_.y, this.vector_.z);", + " this.previousBuffer.setXYZ(index + 1, this.vector_.x, this.vector_.y, this.vector_.z);", + " }", + " if (system.worldSpace) {", + " this.nextBuffer.setXYZ(index, next.position.x, next.position.y, next.position.z);", + " this.nextBuffer.setXYZ(index + 1, next.position.x, next.position.y, next.position.z);", + " }", + " else {", + " if (particle.parentMatrix) {", + " this.vector_.copy(next.position).applyMatrix4(particle.parentMatrix);", + " }", + " else {", + " this.vector_.copy(next.position).applyMatrix4(system.emitter.matrixWorld);", + " }", + " this.nextBuffer.setXYZ(index, this.vector_.x, this.vector_.y, this.vector_.z);", + " this.nextBuffer.setXYZ(index + 1, this.vector_.x, this.vector_.y, this.vector_.z);", + " }", + " this.sideBuffer.setX(index, -1);", + " this.sideBuffer.setX(index + 1, 1);", + " if (system.worldSpace) {", + " this.widthBuffer.setX(index, current.size);", + " this.widthBuffer.setX(index + 1, current.size);", + " }", + " else {", + " if (particle.parentMatrix) {", + " this.widthBuffer.setX(index, current.size);", + " this.widthBuffer.setX(index + 1, current.size);", + " }", + " else {", + " const objectScale = (Math.abs(scale.x) + Math.abs(scale.y) + Math.abs(scale.z)) / 3;", + " this.widthBuffer.setX(index, current.size * objectScale);", + " this.widthBuffer.setX(index + 1, current.size * objectScale);", + " }", + " }", + " this.uvBuffer.setXY(index, (i / particle.previous.length + col) * tileWidth, (vTileCount - row - 1) * tileHeight);", + " this.uvBuffer.setXY(index + 1, (i / particle.previous.length + col) * tileWidth, (vTileCount - row) * tileHeight);", + " this.colorBuffer.setXYZW(index, current.color.x, current.color.y, current.color.z, current.color.w);", + " this.colorBuffer.setXYZW(index + 1, current.color.x, current.color.y, current.color.z, current.color.w);", + " if (i + 1 < particle.previous.length) {", + " this.indexBuffer.setX(triangles * 3, index);", + " this.indexBuffer.setX(triangles * 3 + 1, index + 1);", + " this.indexBuffer.setX(triangles * 3 + 2, index + 2);", + " triangles++;", + " this.indexBuffer.setX(triangles * 3, index + 2);", + " this.indexBuffer.setX(triangles * 3 + 1, index + 1);", + " this.indexBuffer.setX(triangles * 3 + 2, index + 3);", + " triangles++;", + " }", + " previous = current;", + " current = next;", + " if (!curIter.done) {", + " curIter = iter.next();", + " if (curIter.value !== undefined) {", + " next = curIter.value;", + " }", + " }", + " }", + " }", + " });", + " this.positionBuffer.clearUpdateRanges();", + " this.positionBuffer.addUpdateRange(0, index * 3);", + " this.positionBuffer.needsUpdate = true;", + " this.previousBuffer.clearUpdateRanges();", + " this.previousBuffer.addUpdateRange(0, index * 3);", + " this.previousBuffer.needsUpdate = true;", + " this.nextBuffer.clearUpdateRanges();", + " this.nextBuffer.addUpdateRange(0, index * 3);", + " this.nextBuffer.needsUpdate = true;", + " this.sideBuffer.clearUpdateRanges();", + " this.sideBuffer.addUpdateRange(0, index);", + " this.sideBuffer.needsUpdate = true;", + " this.widthBuffer.clearUpdateRanges();", + " this.widthBuffer.addUpdateRange(0, index);", + " this.widthBuffer.needsUpdate = true;", + " this.uvBuffer.clearUpdateRanges();", + " this.uvBuffer.addUpdateRange(0, index * 2);", + " this.uvBuffer.needsUpdate = true;", + " this.colorBuffer.clearUpdateRanges();", + " this.colorBuffer.addUpdateRange(0, index * 4);", + " this.colorBuffer.needsUpdate = true;", + " this.indexBuffer.clearUpdateRanges();", + " this.indexBuffer.addUpdateRange(0, triangles * 3);", + " this.indexBuffer.needsUpdate = true;", + " this.geometry.setDrawRange(0, triangles * 3);", + " }", + " dispose() {", + " this.geometry.dispose();", + " }", + "}", + "", + "class BatchedRenderer extends THREE.Object3D {", + " constructor() {", + " super();", + " this.batches = [];", + " this.systemToBatchIndex = new Map();", + " this.type = 'BatchedRenderer';", + " }", + " static equals(a, b) {", + " return (a.material.side === b.material.side &&", + " a.material.blending === b.material.blending &&", + " a.material.transparent === b.material.transparent &&", + " a.material.type === b.material.type &&", + " a.material.alphaTest === b.material.alphaTest &&", + " a.material.map === b.material.map &&", + " a.renderMode === b.renderMode &&", + " a.uTileCount === b.uTileCount &&", + " a.vTileCount === b.vTileCount &&", + " a.instancingGeometry === b.instancingGeometry &&", + " a.renderOrder === b.renderOrder &&", + " a.layers.mask === b.layers.mask);", + " }", + " addSystem(system) {", + " system._renderer = this;", + " const settings = system.getRendererSettings();", + " for (let i = 0; i < this.batches.length; i++) {", + " if (BatchedRenderer.equals(this.batches[i].settings, settings)) {", + " this.batches[i].addSystem(system);", + " this.systemToBatchIndex.set(system, i);", + " return;", + " }", + " }", + " let batch;", + " switch (settings.renderMode) {", + " case RenderMode.Trail:", + " batch = new TrailBatch(settings);", + " break;", + " case RenderMode.Mesh:", + " case RenderMode.BillBoard:", + " case RenderMode.VerticalBillBoard:", + " case RenderMode.HorizontalBillBoard:", + " case RenderMode.StretchedBillBoard:", + " batch = new SpriteBatch(settings);", + " break;", + " }", + " batch.addSystem(system);", + " this.batches.push(batch);", + " this.systemToBatchIndex.set(system, this.batches.length - 1);", + " this.add(batch);", + " }", + " deleteSystem(system) {", + " const batchIndex = this.systemToBatchIndex.get(system);", + " if (batchIndex != undefined) {", + " this.batches[batchIndex].removeSystem(system);", + " this.systemToBatchIndex.delete(system);", + " }", + " }", + " updateSystem(system) {", + " this.deleteSystem(system);", + " this.addSystem(system);", + " }", + " update(delta) {", + " this.systemToBatchIndex.forEach((value, ps) => {", + " ps.update(delta);", + " });", + " for (let i = 0; i < this.batches.length; i++) {", + " this.batches[i].update();", + " }", + " }", + "}", + "", + "const BatchedParticleRenderer = BatchedRenderer;", + "", + "class QuarksLoader extends THREE.ObjectLoader {", + " constructor(manager) {", + " super(manager);", + " }", + " linkReference(object) {", + " const objectsMap = {};", + " object.traverse(function (child) {", + " objectsMap[child.uuid] = child;", + " });", + " object.traverse(function (child) {", + " if (child.type === 'ParticleEmitter') {", + " const system = child.system;", + " system.emitterShape;", + " for (let i = 0; i < system.behaviors.length; i++) {", + " if (system.behaviors[i] instanceof EmitSubParticleSystem) {", + " system.behaviors[i].subParticleSystem = objectsMap[system.behaviors[i].subParticleSystem];", + " }", + " }", + " }", + " });", + " }", + " parse(json, onLoad) {", + " const object = super.parse(json, onLoad);", + " this.linkReference(object);", + " return object;", + " }", + " parseObject(data, geometries, materials, textures, animations) {", + " let object;", + " function getGeometry(name) {", + " if (geometries[name] === undefined) {", + " console.warn('THREE.ObjectLoader: Undefined geometry', name);", + " }", + " return geometries[name];", + " }", + " function getMaterial(name) {", + " if (name === undefined)", + " return undefined;", + " if (Array.isArray(name)) {", + " const array = [];", + " for (let i = 0, l = name.length; i < l; i++) {", + " const uuid = name[i];", + " if (materials[uuid] === undefined) {", + " console.warn('THREE.ObjectLoader: Undefined material', uuid);", + " }", + " array.push(materials[uuid]);", + " }", + " return array;", + " }", + " if (materials[name] === undefined) {", + " console.warn('THREE.ObjectLoader: Undefined material', name);", + " }", + " return materials[name];", + " }", + " function getTexture(uuid) {", + " if (textures[uuid] === undefined) {", + " console.warn('THREE.ObjectLoader: Undefined texture', uuid);", + " }", + " return textures[uuid];", + " }", + " let geometry, material;", + " const meta = {", + " textures: textures,", + " geometries: geometries,", + " materials: materials,", + " };", + " const dependencies = {};", + " switch (data.type) {", + " case 'ParticleEmitter':", + " object = ParticleSystem.fromJSON(data.ps, meta, dependencies).emitter;", + " break;", + " case 'Scene':", + " object = new THREE.Scene();", + " if (data.background !== undefined) {", + " if (Number.isInteger(data.background)) {", + " object.background = new THREE.Color(data.background);", + " }", + " else {", + " object.background = getTexture(data.background);", + " }", + " }", + " if (data.environment !== undefined) {", + " object.environment = getTexture(data.environment);", + " }", + " if (data.fog !== undefined) {", + " if (data.fog.type === 'Fog') {", + " object.fog = new THREE.Fog(data.fog.color, data.fog.near, data.fog.far);", + " }", + " else if (data.fog.type === 'FogExp2') {", + " object.fog = new THREE.FogExp2(data.fog.color, data.fog.density);", + " }", + " }", + " if (data.backgroundBlurriness !== undefined)", + " object.backgroundBlurriness = data.backgroundBlurriness;", + " break;", + " case 'PerspectiveCamera':", + " object = new THREE.PerspectiveCamera(data.fov, data.aspect, data.near, data.far);", + " if (data.focus !== undefined)", + " object.focus = data.focus;", + " if (data.zoom !== undefined)", + " object.zoom = data.zoom;", + " if (data.filmGauge !== undefined)", + " object.filmGauge = data.filmGauge;", + " if (data.filmOffset !== undefined)", + " object.filmOffset = data.filmOffset;", + " if (data.view !== undefined)", + " object.view = Object.assign({}, data.view);", + " break;", + " case 'OrthographicCamera':", + " object = new THREE.OrthographicCamera(data.left, data.right, data.top, data.bottom, data.near, data.far);", + " if (data.zoom !== undefined)", + " object.zoom = data.zoom;", + " if (data.view !== undefined)", + " object.view = Object.assign({}, data.view);", + " break;", + " case 'AmbientLight':", + " object = new THREE.AmbientLight(data.color, data.intensity);", + " break;", + " case 'DirectionalLight':", + " object = new THREE.DirectionalLight(data.color, data.intensity);", + " break;", + " case 'PointLight':", + " object = new THREE.PointLight(data.color, data.intensity, data.distance, data.decay);", + " break;", + " case 'RectAreaLight':", + " object = new THREE.RectAreaLight(data.color, data.intensity, data.width, data.height);", + " break;", + " case 'SpotLight':", + " object = new THREE.SpotLight(data.color, data.intensity, data.distance, data.angle, data.penumbra, data.decay);", + " break;", + " case 'HemisphereLight':", + " object = new THREE.HemisphereLight(data.color, data.groundColor, data.intensity);", + " break;", + " case 'LightProbe':", + " object = new THREE.LightProbe().fromJSON(data);", + " break;", + " case 'SkinnedMesh':", + " geometry = getGeometry(data.geometry);", + " material = getMaterial(data.material);", + " object = new THREE.SkinnedMesh(geometry, material);", + " if (data.bindMode !== undefined)", + " object.bindMode = data.bindMode;", + " if (data.bindMatrix !== undefined)", + " object.bindMatrix.fromArray(data.bindMatrix);", + " if (data.skeleton !== undefined)", + " object.skeleton = data.skeleton;", + " break;", + " case 'Mesh':", + " geometry = getGeometry(data.geometry);", + " material = getMaterial(data.material);", + " object = new THREE.Mesh(geometry, material);", + " break;", + " case 'InstancedMesh': {", + " geometry = getGeometry(data.geometry);", + " material = getMaterial(data.material);", + " const count = data.count;", + " const instanceMatrix = data.instanceMatrix;", + " const instanceColor = data.instanceColor;", + " object = new THREE.InstancedMesh(geometry, material, count);", + " object.instanceMatrix = new THREE.InstancedBufferAttribute(new Float32Array(instanceMatrix.array), 16);", + " if (instanceColor !== undefined)", + " object.instanceColor = new THREE.InstancedBufferAttribute(new Float32Array(instanceColor.array), instanceColor.itemSize);", + " break;", + " }", + " case 'LOD':", + " object = new THREE.LOD();", + " break;", + " case 'Line':", + " object = new THREE.Line(getGeometry(data.geometry), getMaterial(data.material));", + " break;", + " case 'LineLoop':", + " object = new THREE.LineLoop(getGeometry(data.geometry), getMaterial(data.material));", + " break;", + " case 'LineSegments':", + " object = new THREE.LineSegments(getGeometry(data.geometry), getMaterial(data.material));", + " break;", + " case 'PointCloud':", + " case 'Points':", + " object = new THREE.Points(getGeometry(data.geometry), getMaterial(data.material));", + " break;", + " case 'Sprite':", + " object = new THREE.Sprite(getMaterial(data.material));", + " break;", + " case 'Group':", + " object = new THREE.Group();", + " break;", + " case 'Bone':", + " object = new THREE.Bone();", + " break;", + " default:", + " object = new THREE.Object3D();", + " }", + " object.uuid = data.uuid;", + " if (data.name !== undefined)", + " object.name = data.name;", + " if (data.matrix !== undefined) {", + " object.matrix.fromArray(data.matrix);", + " if (data.matrixAutoUpdate !== undefined)", + " object.matrixAutoUpdate = data.matrixAutoUpdate;", + " if (object.matrixAutoUpdate)", + " object.matrix.decompose(object.position, object.quaternion, object.scale);", + " }", + " else {", + " if (data.position !== undefined)", + " object.position.fromArray(data.position);", + " if (data.rotation !== undefined)", + " object.rotation.fromArray(data.rotation);", + " if (data.quaternion !== undefined)", + " object.quaternion.fromArray(data.quaternion);", + " if (data.scale !== undefined)", + " object.scale.fromArray(data.scale);", + " }", + " if (data.castShadow !== undefined)", + " object.castShadow = data.castShadow;", + " if (data.receiveShadow !== undefined)", + " object.receiveShadow = data.receiveShadow;", + " if (data.shadow) {", + " if (data.shadow.bias !== undefined)", + " object.shadow.bias = data.shadow.bias;", + " if (data.shadow.normalBias !== undefined)", + " object.normalBias = data.shadow.normalBias;", + " if (data.shadow.radius !== undefined)", + " object.radius = data.shadow.radius;", + " if (data.shadow.mapSize !== undefined)", + " object.mapSize.fromArray(data.shadow.mapSize);", + " if (data.shadow.camera !== undefined) {", + " object.camera = this.parseObject(data.shadow.camera);", + " }", + " }", + " if (data.visible !== undefined)", + " object.visible = data.visible;", + " if (data.frustumCulled !== undefined)", + " object.frustumCulled = data.frustumCulled;", + " if (data.renderOrder !== undefined)", + " object.renderOrder = data.renderOrder;", + " if (data.userData !== undefined)", + " object.userData = data.userData;", + " if (data.layers !== undefined)", + " object.layers.mask = data.layers;", + " if (data.children !== undefined) {", + " const children = data.children;", + " for (let i = 0; i < children.length; i++) {", + " object.add(this.parseObject(children[i], geometries, materials, textures, animations));", + " }", + " }", + " if (data.animations !== undefined) {", + " const objectAnimations = data.animations;", + " for (let i = 0; i < objectAnimations.length; i++) {", + " const uuid = objectAnimations[i];", + " object.animations.push(animations[uuid]);", + " }", + " }", + " if (data.type === 'LOD') {", + " if (data.autoUpdate !== undefined)", + " object.autoUpdate = data.autoUpdate;", + " const levels = data.levels;", + " for (let l = 0; l < levels.length; l++) {", + " const level = levels[l];", + " const child = object.getObjectByProperty('uuid', level.object);", + " if (child !== undefined) {", + " object.addLevel(child, level.distance);", + " }", + " }", + " }", + " return object;", + " }", + "}", + "", + "const Plugins = [];", + "function loadPlugin(plugin) {", + " const existing = Plugins.find((item) => item.id === plugin.id);", + " if (!existing) {", + " plugin.initialize();", + " for (const emitterShape of plugin.emitterShapes) {", + " if (!EmitterShapes[emitterShape.type]) {", + " EmitterShapes[emitterShape.type] = emitterShape;", + " }", + " }", + " for (const behavior of plugin.behaviors) {", + " if (!BehaviorTypes[behavior.type]) {", + " BehaviorTypes[behavior.type] = behavior;", + " }", + " }", + " }", + "}", + "function unloadPlugin(pluginId) {", + " const plugin = Plugins.find((item) => item.id === pluginId);", + " if (plugin) {", + " for (const emitterShape of plugin.emitterShapes) {", + " if (EmitterShapes[emitterShape.type]) {", + " delete EmitterShapes[emitterShape.type];", + " }", + " }", + " for (const behavior of plugin.behaviors) {", + " if (BehaviorTypes[behavior.type]) {", + " delete BehaviorTypes[behavior.type];", + " }", + " }", + " }", + "}", + "", + "let NodeValueType = void 0;", + "(function (NodeValueType) {", + " NodeValueType[NodeValueType[\"Number\"] = 0] = \"Number\";", + " NodeValueType[NodeValueType[\"Vec2\"] = 1] = \"Vec2\";", + " NodeValueType[NodeValueType[\"Vec3\"] = 2] = \"Vec3\";", + " NodeValueType[NodeValueType[\"Vec4\"] = 3] = \"Vec4\";", + " NodeValueType[NodeValueType[\"Boolean\"] = 4] = \"Boolean\";", + " NodeValueType[NodeValueType[\"AnyType\"] = 5] = \"AnyType\";", + " NodeValueType[NodeValueType[\"NullableAnyType\"] = 6] = \"NullableAnyType\";", + " NodeValueType[NodeValueType[\"EventStream\"] = 7] = \"EventStream\";", + "})(NodeValueType || (NodeValueType = {}));", + "const genDefaultForNodeValueType = (type) => {", + " switch (type) {", + " case NodeValueType.Boolean:", + " return false;", + " case NodeValueType.Number:", + " return 0;", + " case NodeValueType.Vec2:", + " return new THREE.Vector2();", + " case NodeValueType.Vec3:", + " return new THREE.Vector3();", + " case NodeValueType.Vec4:", + " return new THREE.Vector4();", + " case NodeValueType.AnyType:", + " return 0;", + " case NodeValueType.NullableAnyType:", + " return undefined;", + " }", + "};", + "", + "class Node {", + " constructor(type, signatureIndex = -1, data = {}) {", + " this.inputs = [];", + " this.outputs = [];", + " this.signatureIndex = -1;", + " this.position = new THREE.Vector2();", + " this.outputValues = [];", + " this.id = '' + Math.round(Math.random() * 100000);", + " this.type = type;", + " this.signatureIndex = signatureIndex;", + " this.data = data;", + " const realIndex = signatureIndex === -1 ? 0 : signatureIndex;", + " for (let i = 0; i < type.nodeTypeSignatures[realIndex].inputTypes.length; i++) {", + " this.inputs.push(undefined);", + " }", + " for (let i = 0; i < type.nodeTypeSignatures[realIndex].outputTypes.length; i++) {", + " this.outputs.push([]);", + " this.outputValues.push(genDefaultForNodeValueType(type.nodeTypeSignatures[realIndex].outputTypes[i]));", + " }", + " }", + " get inputTypes() {", + " const signatureIndex = this.signatureIndex === -1 ? 0 : this.signatureIndex;", + " return this.type.nodeTypeSignatures[signatureIndex].inputTypes;", + " }", + " get outputTypes() {", + " const signatureIndex = this.signatureIndex === -1 ? 0 : this.signatureIndex;", + " return this.type.nodeTypeSignatures[signatureIndex].outputTypes;", + " }", + " func(context, inputs, outputs) {", + " const signatureIndex = this.signatureIndex === -1 ? 0 : this.signatureIndex;", + " this.type.nodeTypeSignatures[signatureIndex].func(context, this.data, inputs, outputs);", + " }", + "}", + "class Wire {", + " constructor(input, inputIndex, output, outputIndex) {", + " this.input = input;", + " this.inputIndex = inputIndex;", + " this.input.outputs[inputIndex].push(this);", + " this.output = output;", + " this.outputIndex = outputIndex;", + " this.output.inputs[outputIndex] = this;", + " }", + "}", + "", + "class BaseCompiler {", + " constructor() {", + " this.visited = new Set();", + " BaseCompiler.Instance = this;", + " }", + " buildExecutionOrder(graph, context) {", + " graph.nodesInOrder.length = 0;", + " this.visited.clear();", + " for (let i = 0; i < graph.outputNodes.length; i++) {", + " const node = graph.outputNodes[i];", + " if (node.inputs[0] !== undefined) {", + " this._traverse(node, graph, context);", + " }", + " }", + " graph.compiled = true;", + " }", + " _traverse(node, graph, context) {", + " this.visited.add(node.id);", + " for (let i = 0; i < node.inputs.length; i++) {", + " if (node.inputs[i] instanceof Wire) {", + " const inputNode = node.inputs[i].input;", + " if (!this.visited.has(inputNode.id)) {", + " this._traverse(inputNode, graph, context);", + " }", + " }", + " else if (node.inputs[i] !== undefined);", + " else;", + " }", + " graph.nodesInOrder.push(node);", + " }", + "}", + "", + "class Interpreter extends BaseCompiler {", + " constructor() {", + " super();", + " }", + " executeCompiledGraph(graph, context) {", + " const nodes = graph.nodesInOrder;", + " for (let i = 0; i < nodes.length; i++) {", + " const inputValues = [];", + " const node = nodes[i];", + " for (let j = 0; j < node.inputs.length; j++) {", + " if (node.inputs[j] instanceof Wire) {", + " inputValues.push(node.inputs[j].input.outputValues[node.inputs[j].inputIndex]);", + " }", + " else if (node.inputs[j] !== undefined) {", + " inputValues.push(node.inputs[j].getValue(context));", + " }", + " else {", + " inputValues.push(undefined);", + " }", + " }", + " node.func(context, inputValues, node.outputValues);", + " }", + " }", + " run(graph, context) {", + " if (!graph.compiled) {", + " this.buildExecutionOrder(graph, context);", + " }", + " this.executeCompiledGraph(graph, context);", + " }", + "}", + "", + "class NodeType {", + " constructor(name) {", + " this.nodeTypeSignatures = [];", + " this.name = name;", + " }", + " addSignature(inputTypes, outputTypes, func) {", + " this.nodeTypeSignatures.push({", + " inputTypes: inputTypes,", + " outputTypes: outputTypes,", + " func: func,", + " });", + " }", + "}", + "class GraphNodeType extends NodeType {", + " constructor(nodeGraph) {", + " const inputTypes = [];", + " for (let i = 0; i < nodeGraph.inputNodes.length; i++) {", + " if (nodeGraph.inputNodes[i].type.name === 'input') {", + " inputTypes.push(nodeGraph.inputNodes[i].data.type);", + " }", + " }", + " const outputTypes = [];", + " for (let i = 0; i < nodeGraph.outputNodes.length; i++) {", + " if (nodeGraph.outputNodes[i].type.name === 'output') {", + " outputTypes.push(nodeGraph.outputNodes[i].data.type);", + " }", + " }", + " super(nodeGraph.name);", + " this.addSignature(inputTypes, outputTypes, (context, data, inputs, outputs) => {", + " context.inputs = inputs;", + " context.outputs = outputs;", + " Interpreter.Instance.run(nodeGraph, context);", + " });", + " this.nodeGraph = nodeGraph;", + " }", + "}", + "", + "const NodeTypes = {};", + "const addNode = new NodeType('add');", + "addNode.addSignature([NodeValueType.Number, NodeValueType.Number], [NodeValueType.Number], (context, data, inputs, outputs) => {", + " outputs[0] = inputs[0] + inputs[1];", + "});", + "addNode.addSignature([NodeValueType.Vec2, NodeValueType.Vec2], [NodeValueType.Vec2], (context, data, inputs, outputs) => {", + " outputs[0].addVectors(inputs[0], inputs[1]);", + "});", + "addNode.addSignature([NodeValueType.Vec3, NodeValueType.Vec3], [NodeValueType.Vec3], (context, data, inputs, outputs) => {", + " outputs[0].addVectors(inputs[0], inputs[1]);", + "});", + "addNode.addSignature([NodeValueType.Vec4, NodeValueType.Vec4], [NodeValueType.Vec4], (context, data, inputs, outputs) => {", + " outputs[0].addVectors(inputs[0], inputs[1]);", + "});", + "NodeTypes['add'] = addNode;", + "const subNode = new NodeType('sub');", + "subNode.addSignature([NodeValueType.Number, NodeValueType.Number], [NodeValueType.Number], (context, data, inputs, outputs) => {", + " outputs[0] = inputs[0] - inputs[1];", + "});", + "subNode.addSignature([NodeValueType.Vec2, NodeValueType.Vec2], [NodeValueType.Vec2], (context, data, inputs, outputs) => {", + " outputs[0].subVectors(inputs[0], inputs[1]);", + "});", + "subNode.addSignature([NodeValueType.Vec3, NodeValueType.Vec3], [NodeValueType.Vec3], (context, data, inputs, outputs) => {", + " outputs[0].subVectors(inputs[0], inputs[1]);", + "});", + "subNode.addSignature([NodeValueType.Vec4, NodeValueType.Vec4], [NodeValueType.Vec4], (context, data, inputs, outputs) => {", + " outputs[0].subVectors(inputs[0], inputs[1]);", + "});", + "NodeTypes['sub'] = subNode;", + "const mulNode = new NodeType('mul');", + "mulNode.addSignature([NodeValueType.Number, NodeValueType.Number], [NodeValueType.Number], (context, data, inputs, outputs) => {", + " outputs[0] = inputs[0] * inputs[1];", + "});", + "mulNode.addSignature([NodeValueType.Vec2, NodeValueType.Number], [NodeValueType.Vec2], (context, data, inputs, outputs) => {", + " outputs[0].copy(inputs[0]).multiplyScalar(inputs[1]);", + "});", + "mulNode.addSignature([NodeValueType.Vec3, NodeValueType.Number], [NodeValueType.Vec3], (context, data, inputs, outputs) => {", + " outputs[0].copy(inputs[0]).multiplyScalar(inputs[1]);", + "});", + "mulNode.addSignature([NodeValueType.Vec4, NodeValueType.Number], [NodeValueType.Vec4], (context, data, inputs, outputs) => {", + " outputs[0].copy(inputs[0]).multiplyScalar(inputs[1]);", + "});", + "NodeTypes['mul'] = mulNode;", + "const divNode = new NodeType('div');", + "divNode.addSignature([NodeValueType.Number, NodeValueType.Number], [NodeValueType.Number], (context, data, inputs, outputs) => {", + " outputs[0] = inputs[0] / inputs[1];", + "});", + "divNode.addSignature([NodeValueType.Vec2, NodeValueType.Number], [NodeValueType.Vec2], (context, data, inputs, outputs) => {", + " outputs[0].copy(inputs[0]).divideScalar(inputs[1]);", + "});", + "divNode.addSignature([NodeValueType.Vec3, NodeValueType.Number], [NodeValueType.Vec3], (context, data, inputs, outputs) => {", + " outputs[0].copy(inputs[0]).divideScalar(inputs[1]);", + "});", + "divNode.addSignature([NodeValueType.Vec4, NodeValueType.Number], [NodeValueType.Vec4], (context, data, inputs, outputs) => {", + " outputs[0].copy(inputs[0]).divideScalar(inputs[1]);", + "});", + "NodeTypes['div'] = divNode;", + "const sinNode = new NodeType('sin');", + "sinNode.addSignature([NodeValueType.Number], [NodeValueType.Number], (context, data, inputs, outputs) => {", + " outputs[0] = Math.sin(inputs[0]);", + "});", + "NodeTypes['sin'] = sinNode;", + "const cosNode = new NodeType('cos');", + "cosNode.addSignature([NodeValueType.Number], [NodeValueType.Number], (context, data, inputs, outputs) => {", + " outputs[0] = Math.cos(inputs[0]);", + "});", + "NodeTypes['cos'] = cosNode;", + "const tanNode = new NodeType('tan');", + "tanNode.addSignature([NodeValueType.Number], [NodeValueType.Number], (context, data, inputs, outputs) => {", + " outputs[0] = Math.tan(inputs[0]);", + "});", + "NodeTypes['tan'] = tanNode;", + "const absNode = new NodeType('abs');", + "absNode.addSignature([NodeValueType.Number], [NodeValueType.Number], (context, data, inputs, outputs) => {", + " outputs[0] = Math.abs(inputs[0]);", + "});", + "NodeTypes['abs'] = absNode;", + "const minNode = new NodeType('min');", + "minNode.addSignature([NodeValueType.Number, NodeValueType.Number], [NodeValueType.Number], (context, data, inputs, outputs) => {", + " outputs[0] = Math.min(inputs[0], inputs[1]);", + "});", + "NodeTypes['min'] = minNode;", + "const maxNode = new NodeType('max');", + "maxNode.addSignature([NodeValueType.Number, NodeValueType.Number], [NodeValueType.Number], (context, data, inputs, outputs) => {", + " outputs[0] = Math.max(inputs[0], inputs[1]);", + "});", + "NodeTypes['max'] = maxNode;", + "const dot = new NodeType('dot');", + "dot.addSignature([NodeValueType.Vec2, NodeValueType.Vec2], [NodeValueType.Number], (context, data, inputs, outputs) => {", + " outputs[0] = inputs[0].dot(inputs[1]);", + "});", + "dot.addSignature([NodeValueType.Vec3, NodeValueType.Vec3], [NodeValueType.Number], (context, data, inputs, outputs) => {", + " outputs[0] = inputs[0].dot(inputs[1]);", + "});", + "dot.addSignature([NodeValueType.Vec4, NodeValueType.Vec4], [NodeValueType.Number], (context, data, inputs, outputs) => {", + " outputs[0] = inputs[0].dot(inputs[1]);", + "});", + "NodeTypes['dot'] = dot;", + "const cross = new NodeType('cross');", + "cross.addSignature([NodeValueType.Vec3, NodeValueType.Vec3], [NodeValueType.Vec3], (context, data, inputs, outputs) => {", + " outputs[0].crossVectors(inputs[0], inputs[1]);", + "});", + "NodeTypes['cross'] = cross;", + "const length = new NodeType('length');", + "length.addSignature([NodeValueType.Vec2], [NodeValueType.Number], (context, data, inputs, outputs) => {", + " outputs[0] = inputs[0].length();", + "});", + "length.addSignature([NodeValueType.Vec3], [NodeValueType.Number], (context, data, inputs, outputs) => {", + " outputs[0] = inputs[0].length();", + "});", + "length.addSignature([NodeValueType.Vec4], [NodeValueType.Number], (context, data, inputs, outputs) => {", + " outputs[0] = inputs[0].length();", + "});", + "NodeTypes['length'] = length;", + "const lengthSq = new NodeType('lengthSq');", + "lengthSq.addSignature([NodeValueType.Vec2], [NodeValueType.Number], (context, data, inputs, outputs) => {", + " outputs[0] = inputs[0].lengthSq();", + "});", + "lengthSq.addSignature([NodeValueType.Vec3], [NodeValueType.Number], (context, data, inputs, outputs) => {", + " outputs[0] = inputs[0].lengthSq();", + "});", + "lengthSq.addSignature([NodeValueType.Vec4], [NodeValueType.Number], (context, data, inputs, outputs) => {", + " outputs[0] = inputs[0].lengthSq();", + "});", + "NodeTypes['lengthSq'] = lengthSq;", + "const normalize = new NodeType('normalize');", + "normalize.addSignature([NodeValueType.Vec2], [NodeValueType.Vec2], (context, data, inputs, outputs) => {", + " outputs[0].copy(inputs[0]).normalize();", + "});", + "normalize.addSignature([NodeValueType.Vec3], [NodeValueType.Vec3], (context, data, inputs, outputs) => {", + " outputs[0].copy(inputs[0]).normalize();", + "});", + "normalize.addSignature([NodeValueType.Vec4], [NodeValueType.Vec4], (context, data, inputs, outputs) => {", + " outputs[0].copy(inputs[0]).normalize();", + "});", + "NodeTypes['normalize'] = normalize;", + "const distance = new NodeType('distance');", + "distance.addSignature([NodeValueType.Vec2, NodeValueType.Vec2], [NodeValueType.Number], (context, data, inputs, outputs) => {", + " outputs[0] = inputs[0].distanceTo(inputs[1]);", + "});", + "distance.addSignature([NodeValueType.Vec3, NodeValueType.Vec3], [NodeValueType.Number], (context, data, inputs, outputs) => {", + " outputs[0] = inputs[0].distanceTo(inputs[1]);", + "});", + "NodeTypes['distance'] = distance;", + "const andNode = new NodeType('and');", + "andNode.addSignature([NodeValueType.Boolean, NodeValueType.Boolean], [NodeValueType.Boolean], (context, data, inputs, outputs) => {", + " outputs[0] = inputs[0] && inputs[1];", + "});", + "NodeTypes['and'] = andNode;", + "const orNode = new NodeType('or');", + "orNode.addSignature([NodeValueType.Boolean, NodeValueType.Boolean], [NodeValueType.Boolean], (context, data, inputs, outputs) => {", + " outputs[0] = inputs[0] || inputs[1];", + "});", + "NodeTypes['or'] = orNode;", + "const notNode = new NodeType('not');", + "notNode.addSignature([NodeValueType.Boolean], [NodeValueType.Boolean], (context, data, inputs, outputs) => {", + " outputs[0] = !inputs[0];", + "});", + "NodeTypes['not'] = notNode;", + "const equalNode = new NodeType('equal');", + "equalNode.addSignature([NodeValueType.Number, NodeValueType.Number], [NodeValueType.Boolean], (context, data, inputs, outputs) => {", + " outputs[0] = inputs[0] === inputs[1];", + "});", + "equalNode.addSignature([NodeValueType.Vec2, NodeValueType.Vec2], [NodeValueType.Boolean], (context, data, inputs, outputs) => {", + " outputs[0] = inputs[0].equals(inputs[1]);", + "});", + "equalNode.addSignature([NodeValueType.Vec3, NodeValueType.Vec3], [NodeValueType.Boolean], (context, data, inputs, outputs) => {", + " outputs[0] = inputs[0].equals(inputs[1]);", + "});", + "equalNode.addSignature([NodeValueType.Vec4, NodeValueType.Vec4], [NodeValueType.Boolean], (context, data, inputs, outputs) => {", + " outputs[0] = inputs[0].equals(inputs[1]);", + "});", + "NodeTypes['equal'] = equalNode;", + "const lessThanNode = new NodeType('lessThan');", + "lessThanNode.addSignature([NodeValueType.Number, NodeValueType.Number], [NodeValueType.Boolean], (context, data, inputs, outputs) => {", + " outputs[0] = inputs[0] < inputs[1];", + "});", + "NodeTypes['lessThan'] = lessThanNode;", + "const greaterThanNode = new NodeType('greaterThan');", + "greaterThanNode.addSignature([NodeValueType.Number, NodeValueType.Number], [NodeValueType.Boolean], (context, data, inputs, outputs) => {", + " outputs[0] = inputs[0] > inputs[1];", + "});", + "NodeTypes['greaterThan'] = greaterThanNode;", + "const lessThanOrEqualNode = new NodeType('lessThanOrEqual');", + "lessThanOrEqualNode.addSignature([NodeValueType.Number, NodeValueType.Number], [NodeValueType.Boolean], (context, data, inputs, outputs) => {", + " outputs[0] = inputs[0] <= inputs[1];", + "});", + "NodeTypes['lessThanOrEqual'] = lessThanOrEqualNode;", + "const greaterThanOrEqualNode = new NodeType('greaterThanOrEqual');", + "greaterThanOrEqualNode.addSignature([NodeValueType.Number, NodeValueType.Number], [NodeValueType.Boolean], (context, data, inputs, outputs) => {", + " outputs[0] = inputs[0] >= inputs[1];", + "});", + "NodeTypes['greaterThanOrEqual'] = greaterThanOrEqualNode;", + "const ifNode = new NodeType('if');", + "ifNode.addSignature([NodeValueType.Boolean, NodeValueType.AnyType, NodeValueType.AnyType], [NodeValueType.AnyType], (context, data, inputs, outputs) => {", + " outputs[0] = inputs[0] ? inputs[1] : inputs[2];", + "});", + "NodeTypes['if'] = ifNode;", + "const numberNode = new NodeType('number');", + "numberNode.addSignature([], [NodeValueType.Number], (context, data, inputs, outputs) => {", + " outputs[0] = data.value;", + "});", + "NodeTypes['number'] = numberNode;", + "const vec2Node = new NodeType('vec2');", + "vec2Node.addSignature([NodeValueType.Number, NodeValueType.Number], [NodeValueType.Vec2], (context, data, inputs, outputs) => {", + " outputs[0].x = inputs[0];", + " outputs[0].y = inputs[1];", + "});", + "NodeTypes['vec2'] = vec2Node;", + "const vec3Node = new NodeType('vec3');", + "vec3Node.addSignature([NodeValueType.Number, NodeValueType.Number, NodeValueType.Number], [NodeValueType.Vec3], (context, data, inputs, outputs) => {", + " outputs[0].x = inputs[0];", + " outputs[0].y = inputs[1];", + " outputs[0].z = inputs[2];", + "});", + "NodeTypes['vec3'] = vec3Node;", + "const vec4Node = new NodeType('vec4');", + "vec4Node.addSignature([NodeValueType.Number, NodeValueType.Number, NodeValueType.Number, NodeValueType.Number], [NodeValueType.Vec4], (context, data, inputs, outputs) => {", + " outputs[0].x = inputs[0];", + " outputs[0].y = inputs[1];", + " outputs[0].z = inputs[2];", + " outputs[0].w = inputs[3];", + "});", + "NodeTypes['vec4'] = vec4Node;", + "const splitVec2Node = new NodeType('splitVec2');", + "splitVec2Node.addSignature([NodeValueType.Vec2], [NodeValueType.Number, NodeValueType.Number], (context, data, inputs, outputs) => {", + " outputs[0] = inputs[0].x;", + " outputs[1] = inputs[0].y;", + "});", + "NodeTypes['splitVec2'] = splitVec2Node;", + "const splitVec3Node = new NodeType('splitVec3');", + "splitVec3Node.addSignature([NodeValueType.Vec3], [NodeValueType.Number, NodeValueType.Number, NodeValueType.Number], (context, data, inputs, outputs) => {", + " outputs[0] = inputs[0].x;", + " outputs[1] = inputs[0].y;", + " outputs[2] = inputs[0].z;", + "});", + "NodeTypes['splitVec3'] = splitVec3Node;", + "const splitVec4Node = new NodeType('splitVec4');", + "splitVec4Node.addSignature([NodeValueType.Vec4], [NodeValueType.Number, NodeValueType.Number, NodeValueType.Number, NodeValueType.Number], (context, data, inputs, outputs) => {", + " outputs[0] = inputs[0].x;", + " outputs[1] = inputs[0].y;", + " outputs[2] = inputs[0].z;", + " outputs[3] = inputs[0].w;", + "});", + "NodeTypes['splitVec4'] = splitVec4Node;", + "const boolNode = new NodeType('bool');", + "boolNode.addSignature([], [NodeValueType.Boolean], (context, data, inputs, outputs) => {", + " outputs[0] = data.value;", + "});", + "NodeTypes['bool'] = boolNode;", + "const particlePropertyNode = new NodeType('particleProperty');", + "particlePropertyNode.addSignature([NodeValueType.NullableAnyType], [NodeValueType.NullableAnyType], (context, data, inputs, outputs) => {", + " if (inputs[0] !== undefined) {", + " if (typeof inputs[0] === 'object') {", + " context.particle[data.property].copy(inputs[0]);", + " }", + " else {", + " context.particle[data.property] = inputs[0];", + " }", + " }", + " if (context.particle[data.property] !== undefined) {", + " if (typeof outputs[0] === 'object') {", + " outputs[0].copy(context.particle[data.property]);", + " }", + " else {", + " outputs[0] = context.particle[data.property];", + " }", + " }", + "});", + "NodeTypes['particleProperty'] = particlePropertyNode;", + "const emitNode = new NodeType('emit');", + "emitNode.addSignature([NodeValueType.EventStream], [], (context, data, inputs, outputs) => {", + " const arr = inputs[0];", + " for (let i = 0; i < arr.length; i++) {", + " context.signal(i, arr[i]);", + " }", + "});", + "NodeTypes['emit'] = emitNode;", + "const graphPropertyNode = new NodeType('graphProperty');", + "graphPropertyNode.addSignature([NodeValueType.NullableAnyType], [NodeValueType.NullableAnyType], (context, data, inputs, outputs) => {", + " if (inputs[0] !== undefined) {", + " if (typeof inputs[0] === 'object') {", + " context.graph[data.property].copy(inputs[0]);", + " }", + " else {", + " context.graph[data.property] = inputs[0];", + " }", + " }", + " if (context.graph[data.property] !== undefined) {", + " if (typeof outputs[0] === 'object') {", + " outputs[0].copy(context.graph[data.property]);", + " }", + " else {", + " outputs[0] = context.graph[data.property];", + " }", + " }", + "});", + "NodeTypes['graphProperty'] = graphPropertyNode;", + "const startEventNode = new NodeType('startEvent');", + "startEventNode.addSignature([], [NodeValueType.EventStream], (context, data, inputs, outputs) => {", + " outputs[0] = [{ type: 'start' }];", + "});", + "NodeTypes['startEvent'] = startEventNode;", + "const repeaterNode = new NodeType('repeater');", + "repeaterNode.addSignature([NodeValueType.EventStream, NodeValueType.Number], [NodeValueType.EventStream], (context, data, inputs, outputs) => {", + " const arr = inputs[0];", + " const count = inputs[1];", + " const result = [];", + " for (let j = 0; j < arr.length; j++) {", + " for (let i = 0; i < count; i++) {", + " result.push(arr[j]);", + " }", + " }", + " outputs[0] = result;", + "});", + "NodeTypes['repeater'] = repeaterNode;", + "const timeNode = new NodeType('time');", + "timeNode.addSignature([], [NodeValueType.Number], (context, data, inputs, outputs) => {", + " outputs[0] = context.emissionState.time;", + "});", + "NodeTypes['time'] = timeNode;", + "const deltaNode = new NodeType('delta');", + "deltaNode.addSignature([], [NodeValueType.Number], (context, data, inputs, outputs) => {", + " outputs[0] = context.delta;", + "});", + "NodeTypes['delta'] = deltaNode;", + "const outputNode = new NodeType('output');", + "outputNode.addSignature([NodeValueType.Number], [NodeValueType.Number], (context, data, inputs, outputs) => {", + " outputs[0] = inputs[0];", + "});", + "outputNode.addSignature([NodeValueType.Vec2], [NodeValueType.Vec2], (context, data, inputs, outputs) => {", + " outputs[0] = inputs[0];", + "});", + "outputNode.addSignature([NodeValueType.Vec3], [NodeValueType.Vec3], (context, data, inputs, outputs) => {", + " outputs[0] = inputs[0];", + "});", + "outputNode.addSignature([NodeValueType.Vec4], [NodeValueType.Vec4], (context, data, inputs, outputs) => {", + " outputs[0] = inputs[0];", + "});", + "outputNode.addSignature([NodeValueType.Boolean], [NodeValueType.Boolean], (context, data, inputs, outputs) => {", + " outputs[0] = inputs[0];", + "});", + "NodeTypes['output'] = outputNode;", + "const lerpNode = new NodeType('lerp');", + "lerpNode.addSignature([NodeValueType.Number, NodeValueType.Number, NodeValueType.Number], [NodeValueType.Number], (context, data, inputs, outputs) => {", + " outputs[0] =", + " inputs[0] * (1 - inputs[2]) + inputs[1] * inputs[2];", + "});", + "lerpNode.addSignature([NodeValueType.Vec2, NodeValueType.Vec2, NodeValueType.Number], [NodeValueType.Vec2], (context, data, inputs, outputs) => {", + " outputs[0].lerpVectors(inputs[0], inputs[1], inputs[2]);", + "});", + "lerpNode.addSignature([NodeValueType.Vec3, NodeValueType.Vec3, NodeValueType.Number], [NodeValueType.Vec3], (context, data, inputs, outputs) => {", + " outputs[0].lerpVectors(inputs[0], inputs[1], inputs[2]);", + "});", + "lerpNode.addSignature([NodeValueType.Vec4, NodeValueType.Vec4, NodeValueType.Number], [NodeValueType.Vec4], (context, data, inputs, outputs) => {", + " outputs[0].lerpVectors(inputs[0], inputs[1], inputs[2]);", + "});", + "NodeTypes['lerp'] = lerpNode;", + "const normalD = (x) => {", + " return (1 / Math.sqrt(2 * Math.PI)) * Math.exp(x * x * -0.5);", + "};", + "const normalDistributionNode = new NodeType('normDistrib');", + "normalDistributionNode.addSignature([NodeValueType.Number], [NodeValueType.Number], (context, data, inputs, outputs) => {", + " outputs[0] = normalD(inputs[0]);", + "});", + "NodeTypes['normDistrib'] = normalDistributionNode;", + "const normcdfNode = new NodeType('normcdf');", + "normcdfNode.addSignature([NodeValueType.Number], [NodeValueType.Number], (context, data, inputs, outputs) => {", + " let x = inputs[0];", + " const a1 = 0.254829592;", + " const a2 = -0.284496736;", + " const a3 = 1.421413741;", + " const a4 = -1.453152027;", + " const a5 = 1.061405429;", + " const p = 0.3275911;", + " let sign = 1;", + " if (x < 0)", + " sign = -1;", + " x = Math.abs(x) / Math.sqrt(2.0);", + " const t = 1.0 / (1.0 + p * x);", + " const y = 1.0 - ((((a5 * t + a4) * t + a3) * t + a2) * t + a1) * t * Math.exp(-x * x);", + " outputs[0] = 0.5 * (1.0 + sign * y);", + "});", + "NodeTypes['normcdf'] = normcdfNode;", + "const normcdfInvNode = new NodeType('normcdfInv');", + "const rationalApproximation = (t) => {", + " const c = [2.515517, 0.802853, 0.010328];", + " const d = [1.432788, 0.189269, 0.001308];", + " return t - ((c[2] * t + c[1]) * t + c[0]) / (((d[2] * t + d[1]) * t + d[0]) * t + 1.0);", + "};", + "const normcdfInv = (p) => {", + " if (p < 0.5) {", + " return -rationalApproximation(Math.sqrt(-2.0 * Math.log(p)));", + " }", + " else {", + " return rationalApproximation(Math.sqrt(-2.0 * Math.log(1 - p)));", + " }", + "};", + "normcdfInvNode.addSignature([NodeValueType.Number], [NodeValueType.Number], (context, data, inputs, outputs) => {", + " outputs[0] = normcdfInv(inputs[0]);", + "});", + "NodeTypes['normcdfInv'] = normcdfInvNode;", + "const clampNode = new NodeType('clamp');", + "clampNode.addSignature([NodeValueType.Number, NodeValueType.Number, NodeValueType.Number], [NodeValueType.Number], (context, data, inputs, outputs) => {", + " outputs[0] = Math.max(Math.min(inputs[0], inputs[2]), inputs[1]);", + "});", + "NodeTypes['clamp'] = clampNode;", + "const smoothstepNode = new NodeType('smoothstep');", + "smoothstepNode.addSignature([NodeValueType.Number, NodeValueType.Number, NodeValueType.Number], [NodeValueType.Number], (context, data, inputs, outputs) => {", + " const x = Math.max(Math.min(inputs[0], inputs[2]), inputs[1]);", + " outputs[0] = x * x * (3 - 2 * x);", + "});", + "NodeTypes['smoothstep'] = smoothstepNode;", + "const randomNode = new NodeType('random');", + "randomNode.addSignature([NodeValueType.Number, NodeValueType.Number], [NodeValueType.Number], (context, data, inputs, outputs) => {", + " outputs[0] = Math.random() * (inputs[1] - inputs[0]) + inputs[0];", + "});", + "randomNode.addSignature([NodeValueType.Vec2, NodeValueType.Vec2], [NodeValueType.Vec2], (context, data, inputs, outputs) => {", + " let random = Math.random();", + " outputs[0].lerpVectors(inputs[0], inputs[1], random);", + "});", + "randomNode.addSignature([NodeValueType.Vec3, NodeValueType.Vec3], [NodeValueType.Vec3], (context, data, inputs, outputs) => {", + " let random = Math.random();", + " outputs[0].lerpVectors(inputs[0], inputs[1], random);", + "});", + "randomNode.addSignature([NodeValueType.Vec4, NodeValueType.Vec4], [NodeValueType.Vec4], (context, data, inputs, outputs) => {", + " let random = Math.random();", + " outputs[0].lerpVectors(inputs[0], inputs[1], random);", + "});", + "NodeTypes['random'] = randomNode;", + "const vrandNode = new NodeType('vrand');", + "vrandNode.addSignature([], [NodeValueType.Vec2], (context, data, inputs, outputs) => {", + " let x = normcdfInv(Math.random());", + " let y = normcdfInv(Math.random());", + " const mag = Math.sqrt(x * x + y * y);", + " outputs[0].set(x / mag, y / mag);", + "});", + "vrandNode.addSignature([], [NodeValueType.Vec3], (context, data, inputs, outputs) => {", + " let x = normcdfInv(Math.random());", + " let y = normcdfInv(Math.random());", + " let z = normcdfInv(Math.random());", + " const mag = Math.sqrt(x * x + y * y + z * z);", + " outputs[0].set(x / mag, y / mag, z / mag);", + "});", + "vrandNode.addSignature([], [NodeValueType.Vec4], (context, data, inputs, outputs) => {", + " let x = normcdfInv(Math.random());", + " let y = normcdfInv(Math.random());", + " let z = normcdfInv(Math.random());", + " let w = normcdfInv(Math.random());", + " const mag = Math.sqrt(x * x + y * y + z * z + w * w);", + " outputs[0].set(x / mag, y / mag, z / mag, w / mag);", + "});", + "NodeTypes['vrand'] = vrandNode;", + "const bsdfNode = new NodeType('bsdf');", + "bsdfNode.addSignature([NodeValueType.Vec3, NodeValueType.Vec3, NodeValueType.Vec3, NodeValueType.Number], [], (context, data, inputs, outputs) => { });", + "NodeTypes['bsdf'] = bsdfNode;", + "const OutputNodeTypeNames = new Set(['output', 'particleProperty', 'graphProperty', 'emit']);", + "", + "class NodeGraph {", + " constructor(name) {", + " this.inputNodes = [];", + " this.outputNodes = [];", + " this.allNodes = new Map();", + " this.wires = [];", + " this.compiled = false;", + " this.nodesInOrder = [];", + " this.version = '1.0';", + " this.uuid = THREE.MathUtils.generateUUID();", + " this.name = name;", + " this.revision = 0;", + " }", + " addWire(wire) {", + " this.wires.push(wire);", + " this.revision++;", + " }", + " addNode(node) {", + " this.allNodes.set(node.id, node);", + " if (node.type === NodeTypes['input']) {", + " this.inputNodes.push(node);", + " }", + " else if (OutputNodeTypeNames.has(node.type.name)) {", + " this.outputNodes.push(node);", + " }", + " this.revision++;", + " }", + " getNode(id) {", + " return this.allNodes.get(id);", + " }", + " deleteNode(node) {", + " this.allNodes.delete(node.id);", + " this.revision++;", + " }", + " deleteWire(wire) {", + " let index = wire.input.outputs[wire.inputIndex].indexOf(wire);", + " if (index !== -1) {", + " wire.input.outputs[wire.inputIndex].splice(index, 1);", + " }", + " wire.output.inputs[wire.outputIndex] = undefined;", + " index = this.wires.indexOf(wire);", + " if (index != -1) {", + " this.wires[index] = this.wires[this.wires.length - 1];", + " this.wires.pop();", + " }", + " this.revision++;", + " }", + " toJSON() {", + " throw new Error('not implemented');", + " }", + " clone() {", + " throw new Error('not implemented');", + " }", + "}", + "", + "new THREE.Vector3(0, 0, 1);", + "const tempQ = new THREE.Quaternion();", + "const tempV = new THREE.Vector3();", + "const tempV2 = new THREE.Vector3();", + "const PREWARM_FPS = 60;", + "const DEFAULT_GEOMETRY = new THREE.PlaneGeometry(1, 1, 1, 1);", + "class NodeVFX {", + " set time(time) {", + " this.emissionState.time = time;", + " }", + " get time() {", + " return this.emissionState.time;", + " }", + " get layers() {", + " return this.rendererSettings.layers;", + " }", + " get texture() {", + " return this.rendererSettings.material.map;", + " }", + " set texture(texture) {", + " this.rendererSettings.material.map = texture;", + " this.neededToUpdateRender = true;", + " }", + " get material() {", + " return this.rendererSettings.material;", + " }", + " set material(material) {", + " this.rendererSettings.material = material;", + " this.neededToUpdateRender = true;", + " }", + " get instancingGeometry() {", + " return this.rendererSettings.instancingGeometry;", + " }", + " set instancingGeometry(geometry) {", + " this.restart();", + " this.particles.length = 0;", + " this.rendererSettings.instancingGeometry = geometry;", + " this.neededToUpdateRender = true;", + " }", + " get renderMode() {", + " return this.rendererSettings.renderMode;", + " }", + " set renderMode(renderMode) {", + " if ((this.rendererSettings.renderMode != RenderMode.Trail && renderMode === RenderMode.Trail) ||", + " (this.rendererSettings.renderMode == RenderMode.Trail && renderMode !== RenderMode.Trail)) {", + " this.restart();", + " this.particles.length = 0;", + " }", + " if (this.rendererSettings.renderMode !== renderMode) {", + " switch (renderMode) {", + " case RenderMode.Trail:", + " this.rendererEmitterSettings = {", + " startLength: 30,", + " followLocalOrigin: false,", + " };", + " break;", + " case RenderMode.Mesh:", + " this.rendererEmitterSettings = {", + " geometry: new THREE.PlaneGeometry(1, 1),", + " };", + " break;", + " case RenderMode.BillBoard:", + " case RenderMode.VerticalBillBoard:", + " case RenderMode.HorizontalBillBoard:", + " case RenderMode.StretchedBillBoard:", + " this.rendererEmitterSettings = {};", + " break;", + " }", + " }", + " this.rendererSettings.renderMode = renderMode;", + " this.neededToUpdateRender = true;", + " }", + " get renderOrder() {", + " return this.rendererSettings.renderOrder;", + " }", + " set renderOrder(renderOrder) {", + " this.rendererSettings.renderOrder = renderOrder;", + " this.neededToUpdateRender = true;", + " }", + " get blending() {", + " return this.rendererSettings.material.blending;", + " }", + " set blending(blending) {", + " this.rendererSettings.material.blending = blending;", + " this.neededToUpdateRender = true;", + " }", + " constructor(parameters) {", + " var _a, _b, _c, _d, _e, _f, _g;", + " this.temp = new THREE.Vector3();", + " this.travelDistance = 0;", + " this.normalMatrix = new THREE.Matrix3();", + " this.speedFactor = 0;", + " this.autoDestroy = parameters.autoDestroy === undefined ? false : parameters.autoDestroy;", + " this.duration = (_a = parameters.duration) !== null && _a !== void 0 ? _a : 1;", + " this.looping = parameters.looping === undefined ? true : parameters.looping;", + " this.prewarm = parameters.prewarm === undefined ? false : parameters.prewarm;", + " this.worldSpace = (_b = parameters.worldSpace) !== null && _b !== void 0 ? _b : false;", + " this.rendererEmitterSettings = (_c = parameters.rendererEmitterSettings) !== null && _c !== void 0 ? _c : {};", + " this.emissionGraph = parameters.emissionGraph;", + " this.updateGraph = parameters.updateGraph;", + " this.interpreter = new Interpreter();", + " this.rendererSettings = {", + " instancingGeometry: (_d = parameters.instancingGeometry) !== null && _d !== void 0 ? _d : DEFAULT_GEOMETRY,", + " renderMode: (_e = parameters.renderMode) !== null && _e !== void 0 ? _e : RenderMode.BillBoard,", + " renderOrder: (_f = parameters.renderOrder) !== null && _f !== void 0 ? _f : 0,", + " material: parameters.material,", + " layers: (_g = parameters.layers) !== null && _g !== void 0 ? _g : new THREE.Layers(),", + " uTileCount: 1,", + " vTileCount: 1,", + " };", + " this.neededToUpdateRender = true;", + " this.particles = new Array();", + " this.emitter = new ParticleEmitter(this);", + " this.paused = false;", + " this.particleNum = 0;", + " this.emissionState = {", + " time: 0,", + " };", + " this.emitEnded = false;", + " this.markForDestroy = false;", + " this.prewarmed = false;", + " }", + " pause() {", + " this.paused = true;", + " }", + " play() {", + " this.paused = false;", + " }", + " spawn(emissionState, matrix) {", + " tempQ.setFromRotationMatrix(matrix);", + " const translation = tempV;", + " const quaternion = tempQ;", + " const scale = tempV2;", + " matrix.decompose(translation, quaternion, scale);", + " this.particleNum++;", + " while (this.particles.length < this.particleNum) {", + " this.particles.push(new NodeParticle());", + " }", + " const particle = this.particles[this.particleNum - 1];", + " particle.reset();", + " this.interpreter.run(this.updateGraph, { particle: particle, emissionState: this.emissionState });", + " if (this.rendererSettings.renderMode === RenderMode.Trail &&", + " this.rendererEmitterSettings.followLocalOrigin) {", + " const trail = particle;", + " trail.localPosition = new THREE.Vector3().copy(trail.position);", + " }", + " if (this.worldSpace) {", + " particle.position.applyMatrix4(matrix);", + " particle.size *= (Math.abs(scale.x) + Math.abs(scale.y) + Math.abs(scale.z)) / 3;", + " particle.velocity.multiply(scale).applyMatrix3(this.normalMatrix);", + " if (particle.rotation && particle.rotation instanceof THREE.Quaternion) {", + " particle.rotation.multiplyQuaternions(tempQ, particle.rotation);", + " }", + " }", + " }", + " endEmit() {", + " this.emitEnded = true;", + " if (this.autoDestroy) {", + " this.markForDestroy = true;", + " }", + " }", + " dispose() {", + " if (this._renderer)", + " this._renderer.deleteSystem(this);", + " this.emitter.dispose();", + " if (this.emitter.parent)", + " this.emitter.parent.remove(this.emitter);", + " }", + " restart() {", + " this.paused = false;", + " this.particleNum = 0;", + " this.emissionState.time = 0;", + " this.emitEnded = false;", + " this.markForDestroy = false;", + " this.prewarmed = false;", + " }", + " update(delta) {", + " if (this.paused)", + " return;", + " let currentParent = this.emitter;", + " while (currentParent.parent) {", + " currentParent = currentParent.parent;", + " }", + " if (currentParent.type !== 'Scene') {", + " this.dispose();", + " return;", + " }", + " if (this.emitEnded && this.particleNum === 0) {", + " if (this.markForDestroy && this.emitter.parent)", + " this.dispose();", + " return;", + " }", + " if (this.looping && this.prewarm && !this.prewarmed) {", + " this.prewarmed = true;", + " for (let i = 0; i < this.duration * PREWARM_FPS; i++) {", + " this.update(1.0 / PREWARM_FPS);", + " }", + " }", + " if (delta > 0.1) {", + " delta = 0.1;", + " }", + " if (this.neededToUpdateRender) {", + " if (this._renderer)", + " this._renderer.updateSystem(this);", + " this.neededToUpdateRender = false;", + " }", + " this.emit(delta, this.emissionState, this.emitter.matrixWorld);", + " const context = { particle: undefined, emissionState: this.emissionState, delta };", + " for (let i = 0; i < this.particleNum; i++) {", + " context.particle = this.particles[i];", + " this.interpreter.run(this.updateGraph, context);", + " }", + " for (let i = 0; i < this.particleNum; i++) {", + " if (this.rendererEmitterSettings.followLocalOrigin &&", + " this.particles[i].localPosition) {", + " this.particles[i].position.copy(this.particles[i].localPosition);", + " if (this.particles[i].parentMatrix) {", + " this.particles[i].position.applyMatrix4(this.particles[i].parentMatrix);", + " }", + " else {", + " this.particles[i].position.applyMatrix4(this.emitter.matrixWorld);", + " }", + " }", + " else {", + " this.particles[i].position.addScaledVector(this.particles[i].velocity, delta);", + " }", + " this.particles[i].age += delta;", + " }", + " if (this.rendererSettings.renderMode === RenderMode.Trail) {", + " for (let i = 0; i < this.particleNum; i++) {", + " const particle = this.particles[i];", + " particle.update();", + " }", + " }", + " for (let i = 0; i < this.particleNum; i++) {", + " const particle = this.particles[i];", + " if (particle.died && (!(particle instanceof TrailParticle) || particle.previous.length === 0)) {", + " this.particles[i] = this.particles[this.particleNum - 1];", + " this.particles[this.particleNum - 1] = particle;", + " this.particleNum--;", + " i--;", + " }", + " }", + " }", + " emit(delta, emissionState, emitterMatrix) {", + " if (emissionState.time > this.duration) {", + " if (this.looping) {", + " emissionState.time -= this.duration;", + " }", + " else {", + " if (!this.emitEnded) {", + " this.endEmit();", + " }", + " }", + " }", + " this.normalMatrix.getNormalMatrix(emitterMatrix);", + " const context = {", + " signal: () => {", + " this.spawn(emissionState, emitterMatrix);", + " },", + " emissionState,", + " delta,", + " };", + " if (!this.emitEnded) {", + " this.interpreter.run(this.emissionGraph, context);", + " }", + " if (this.previousWorldPos === undefined)", + " this.previousWorldPos = new THREE.Vector3();", + " this.emitter.getWorldPosition(this.previousWorldPos);", + " emissionState.time += delta;", + " }", + " toJSON(meta, options = {}) {", + " return {};", + " }", + " getRendererSettings() {", + " return this.rendererSettings;", + " }", + " clone() {", + " return this;", + " }", + "}", + "", + "gdjs.__particleEmmiter3DExtension = {", + " ParticleEmitter3DRenderer,", + " ParticleEmitterAdapter,", + " ParticleEmitterHelper,", + "", + " ApplyCollision,", + " ApplyForce,", + " ApplySequences,", + " AxisAngleGenerator,", + " BatchedParticleRenderer,", + " BatchedRenderer,", + " BehaviorFromJSON,", + " BehaviorTypes,", + " Bezier,", + " ChangeEmitDirection,", + " ColorGeneratorFromJSON,", + " ColorOverLife,", + " ColorRange,", + " ConeEmitter,", + " ConstantColor,", + " ConstantValue,", + " DonutEmitter,", + " EmitSubParticleSystem,", + " EmitterFromJSON,", + " EmitterMode,", + " EmitterShapes,", + " EulerGenerator,", + " ForceOverLife,", + " FrameOverLife,", + " GeneratorFromJSON,", + " Gradient,", + " GraphNodeType,", + " GravityForce,", + " GridEmitter,", + " Interpreter,", + " IntervalValue,", + " MeshSurfaceEmitter,", + " Node,", + " NodeGraph,", + " NodeType,", + " NodeTypes,", + " NodeValueType,", + " Noise,", + " OrbitOverLife,", + " ParticleEmitter,", + " ParticleSystem,", + " PiecewiseBezier,", + " PiecewiseFunction,", + " Plugins,", + " PointEmitter,", + " QuarksLoader,", + " RandomColor,", + " RandomColorBetweenGradient,", + " RandomQuatGenerator,", + " RecordState,", + " RenderMode,", + " Rotation3DOverLife,", + " RotationGeneratorFromJSON,", + " RotationOverLife,", + " SequencerFromJSON,", + " SizeOverLife,", + " SpeedOverLife,", + " SphereEmitter,", + " SpriteBatch,", + " SpriteParticle,", + " SubParticleEmitMode,", + " TextureSequencer,", + " TrailBatch,", + " TrailParticle,", + " TurbulenceField,", + " VFXBatch,", + " ValueGeneratorFromJSON,", + " WidthOverLength,", + " Wire,", + " genDefaultForNodeValueType,", + " getPhysicsResolver,", + " loadPlugin,", + " setPhysicsResolver,", + " unloadPlugin,", + "}" + ], + "parameterObjects": "", + "useStrict": true, + "eventsSheetExpanded": true + } + ], + "parameters": [], + "objectGroups": [] + }, + { + "fullName": "", + "functionType": "Action", + "name": "onScenePreEvents", + "sentence": "", + "events": [ + { + "type": "BuiltinCommonInstructions::JsCode", + "inlineCode": [ + "\r", + "// See doStepPostEvents\r", + "runtimeScene.__particleEmmiter3DExtension = runtimeScene.__particleEmmiter3DExtension || {};\r", + "runtimeScene.__particleEmmiter3DExtension.emittersStepped = 0;" + ], + "parameterObjects": "", + "useStrict": true, + "eventsSheetExpanded": true + } + ], + "parameters": [], + "objectGroups": [] + } + ], + "eventsFunctionsFolderStructure": { + "folderName": "__ROOT", + "children": [ + { + "functionName": "DefineHelperClasses" + }, + { + "functionName": "onScenePreEvents" + } + ] + }, + "eventsBasedBehaviors": [], + "eventsBasedObjects": [ + { + "areaMaxX": 64, + "areaMaxY": 64, + "areaMaxZ": 64, + "areaMinX": 0, + "areaMinY": 0, + "areaMinZ": 0, + "assetStoreTag": "3d particles", + "defaultName": "ParticleEmitter", + "description": "Display a large number of particles to create visual effects.", + "fullName": "3D particle emitter", + "helpPath": "", + "iconUrl": "", + "is3D": true, + "isUsingLegacyInstancesRenderer": true, + "name": "ParticleEmitter3D", + "previewIconUrl": "", + "objects": [ + { + "adaptCollisionMaskAutomatically": true, + "assetStoreId": "", + "name": "Particle", + "type": "Sprite", + "updateIfNotVisible": false, + "variables": [], + "effects": [], + "behaviors": [], + "animations": [ + { + "name": "Image", + "useMultipleDirections": false, + "directions": [ + { + "looping": false, + "timeBetweenFrames": 0.08, + "sprites": [] + } + ] + } + ] + } + ], + "objectsFolderStructure": { + "folderName": "__ROOT", + "children": [ + { + "objectName": "Particle" + } + ] + }, + "objectsGroups": [], + "layers": [ + { + "ambientLightColorB": 200, + "ambientLightColorG": 200, + "ambientLightColorR": 200, + "camera2DPlaneMaxDrawingDistance": 5000, + "camera3DFarPlaneDistance": 10000, + "camera3DFieldOfView": 45, + "camera3DNearPlaneDistance": 3, + "cameraType": "", + "followBaseLayerCamera": false, + "isLightingLayer": false, + "isLocked": false, + "name": "", + "renderingType": "", + "visibility": true, + "cameras": [ + { + "defaultSize": true, + "defaultViewport": true, + "height": 0, + "viewportBottom": 1, + "viewportLeft": 0, + "viewportRight": 1, + "viewportTop": 0, + "width": 0 + } + ], + "effects": [] + } + ], + "instances": [], + "editionSettings": [], + "eventsFunctions": [ + { + "fullName": "", + "functionType": "Action", + "name": "onCreated", + "sentence": "", + "events": [ + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [], + "actions": [ + { + "type": { + "value": "ParticleEmitter3D::DefineHelperClasses" + }, + "parameters": [ + "", + "" + ] + } + ] + }, + { + "type": "BuiltinCommonInstructions::JsCode", + "inlineCode": [ + "const {", + " ParticleEmitterAdapter,", + " ParticleEmitter3DRenderer,", + " ParticleEmitterHelper,", + "", + " ParticleSystem,", + " IntervalValue,", + " ConstantValue,", + " ConstantColor,", + " ColorOverLife,", + " SizeOverLife,", + " ApplyForce,", + " WidthOverLength,", + " Gradient,", + " PiecewiseBezier,", + " Bezier,", + " PointEmitter,", + " ConeEmitter,", + " RenderMode", + "} = gdjs.__particleEmmiter3DExtension;", + "", + "/** @type {gdjs.CustomRuntimeObject} */", + "const object = objects[0];", + "// Force the size of the object because it doesn't contain any instance.", + "object._innerArea = {", + " min: [-16, -16, -16],", + " max: [16, 16, 16],", + " };", + "const hitbox = new gdjs.Polygon();", + "hitbox.vertices = [[-16, -16], [16, -16], [16, 16], [-16, 16]];", + "object._untransformedHitBoxes = [hitbox];", + "object._unrotatedAABB.min[0] = -16;", + "object._unrotatedAABB.min[1] = -16;", + "object._unrotatedAABB.max[0] = 16;", + "object._unrotatedAABB.max[1] = 16;", + "object._updateUntransformedHitBoxes = () => {};", + "", + "// Here runtimeScene is the gdjs.CustomRuntimeObjectInstanceContainer inside the custom object.", + "const gameScene = object.getRuntimeScene();", + "const game = runtimeScene.getGame();", + "", + "const texture = game.getImageManager().getThreeTexture('');", + "", + "// Set the blending here because changes are not applied after the emitter creation.", + "const blendingString = object._getBlending();", + "const blending =", + " blendingString === \"Additive\" ? THREE.AdditiveBlending :", + " blendingString === \"Normal\" ? THREE.NormalBlending :", + " blendingString === \"Subtractive\" ? THREE.SubtractiveBlending :", + " blendingString === \"Multiply\" ? THREE.MultiplyBlending :", + " blendingString === \"None\" ? THREE.NoBlending :", + " THREE.AdditiveBlending;", + "", + "", + "// Build a configuration with the right kind of objects.", + "// These values are not important as they are overrided by the object properties values.", + "const muzzle = {", + " duration: 10,", + " looping: false,", + " startLife: new IntervalValue(1, 2),", + " startSpeed: new IntervalValue(100, 200),", + " startSize: new IntervalValue(20, 50),", + " startColor: new ConstantColor(new THREE.Vector4(1, 1, 1, 1)),", + " worldSpace: true,", + "", + " maxParticle: 5,", + " emissionOverTime: new ConstantValue(50),", + " emissionBursts: [{", + " time: 0,", + " count: new ConstantValue(1),", + " cycle: 1,", + " interval: 0.01,", + " probability: 1,", + " }],", + "", + " shape: new PointEmitter(),", + " material: new THREE.MeshBasicMaterial({", + " map: texture,", + " blending: blending,", + " transparent: true,", + " side: THREE.DoubleSide", + " }),", + " startTileIndex: 0,", + " uTileCount: 1,", + " vTileCount: 1,", + " renderOrder: 2,", + " renderMode: RenderMode.BillBoard", + "};", + "const particleSystem = new ParticleSystem(muzzle);", + "", + "const colorOverLife = new ColorOverLife(", + " new Gradient(", + " [", + " [new THREE.Vector3(1, 0, 0), 0],", + " [new THREE.Vector3(1, 0, 0), 1],", + " ],", + " [", + " [1, 0],", + " [1, 1],", + " ]", + " ));", + "particleSystem.addBehavior(colorOverLife);", + "const sizeOverLife = new SizeOverLife(new PiecewiseBezier([[new Bezier(1, 2 / 3, 1 / 3, 0), 0]]));", + "particleSystem.addBehavior(sizeOverLife);", + "const applyForce = new ApplyForce(new THREE.Vector3(0, 0, -1), new ConstantValue(0));", + "particleSystem.addBehavior(applyForce);", + "const widthOverLength = new WidthOverLength(new PiecewiseBezier([[new Bezier(1, 2 / 3, 1 / 3, 0), 0]]));", + "particleSystem.addBehavior(widthOverLength);", + "", + "particleSystem.emitter.name = object.getName() + object.getNameId();", + "", + "object.__particleEmitterAdapter = new ParticleEmitterAdapter(particleSystem, colorOverLife, sizeOverLife, applyForce, widthOverLength);", + "object.__particleSystem = particleSystem;", + "", + "// This is a hack that may break in future releases.", + "// Replace the group that would hold children objects by the emmiter.", + "const layer = object.getInstanceContainer().getLayer(object.getLayer());", + "const group = object.getRenderer()._threeGroup;", + "layer.getRenderer().remove3DRendererObject(group);", + "particleSystem.emitter.position.copy(group.position);", + "particleSystem.emitter.rotation.order = 'ZYX';", + "particleSystem.emitter.rotation.copy(group.rotation);", + "", + "const particleEmitter3DRenderer = new ParticleEmitter3DRenderer(object, object._instanceContainer, object.getInstanceContainer());", + "object._renderer = particleEmitter3DRenderer;", + "if (game.isInGameEdition && game.isInGameEdition()) {", + " const particleEmitterHelper = new ParticleEmitterHelper(object);", + " particleEmitter3DRenderer._threeGroup = particleEmitterHelper;", + " layer.getRenderer().add3DRendererObject(particleEmitterHelper);", + "}", + "else {", + " particleEmitter3DRenderer._threeGroup = particleSystem.emitter;", + " layer.getRenderer().add3DRendererObject(particleSystem.emitter);", + "}", + "", + "particleSystem.emitter.updateMatrixWorld(true);", + "", + "", + "// See doStepPostEvents", + "gameScene.__particleEmmiter3DExtension = gameScene.__particleEmmiter3DExtension || {};", + "gameScene.__particleEmmiter3DExtension.emittersCount = gameScene.__particleEmmiter3DExtension.emittersCount || 0;", + "gameScene.__particleEmmiter3DExtension.emittersCount++;", + "", + "" + ], + "parameterObjects": "Object", + "useStrict": true, + "eventsSheetExpanded": true + }, + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [], + "actions": [ + { + "type": { + "value": "ParticleEmitter3D::ParticleEmitter3D::UpdateFromProperties" + }, + "parameters": [ + "Object", + "" + ] + }, + { + "type": { + "value": "ParticleEmitter3D::ParticleEmitter3D::SetRotationCenter" + }, + "parameters": [ + "Object", + "0", + "0", + "0" + ] + } + ] + } + ], + "parameters": [ + { + "description": "Object", + "name": "Object", + "supplementaryInformation": "ParticleEmitter3D::ParticleEmitter3D", + "type": "object" + } + ], + "objectGroups": [] + }, + { + "fullName": "", + "functionType": "Action", + "name": "onDestroy", + "sentence": "", + "events": [ + { + "type": "BuiltinCommonInstructions::JsCode", + "inlineCode": [ + "/** @type {gdjs.CustomRuntimeObject} */", + "const object = objects[0];", + "// Here runtimeScene is the gdjs.CustomRuntimeObjectInstanceContainer inside the custom object.", + "const gameScene = object.getRuntimeScene();", + "", + "object.__particleSystem.dispose();", + "", + "// See doStepPostEvents", + "gameScene.__particleEmmiter3DExtension.emittersCount--;", + "", + "if (gameScene.__particleEmmiter3DExtension.emittersCount === 0) {", + " // Update batch system now because doStepPostEvents won't be called.", + " gameScene.__particleEmmiter3DExtension.layerNames = gameScene.__particleEmmiter3DExtension.layerNames || [];", + " const layerNames = gameScene.__particleEmmiter3DExtension.layerNames;", + " gameScene.getAllLayerNames(layerNames);", + " for (const layerName of layerNames) {", + " const layer = gameScene.getLayer(layerName);", + " if (layer.__particleEmmiter3DExtension) {", + " layer.__particleEmmiter3DExtension.batchSystem.update(layer.getElapsedTime() / 1000);", + " }", + " }", + "}" + ], + "parameterObjects": "Object", + "useStrict": true, + "eventsSheetExpanded": true + } + ], + "parameters": [ + { + "description": "Object", + "name": "Object", + "supplementaryInformation": "ParticleEmitter3D::ParticleEmitter3D", + "type": "object" + } + ], + "objectGroups": [] + }, + { + "fullName": "", + "functionType": "Action", + "name": "onHotReloading", + "sentence": "", + "events": [ + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [], + "actions": [ + { + "type": { + "value": "ParticleEmitter3D::ParticleEmitter3D::UpdateFromProperties" + }, + "parameters": [ + "Object", + "" + ] + } + ] + } + ], + "parameters": [ + { + "description": "Object", + "name": "Object", + "supplementaryInformation": "ParticleEmitter3D::ParticleEmitter3D", + "type": "object" + } + ], + "objectGroups": [] + }, + { + "description": "Update from properties.", + "fullName": "Update from properties", + "functionType": "Action", + "name": "UpdateFromProperties", + "private": true, + "sentence": "Update from properties of _PARAM0_", + "events": [ + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [], + "actions": [ + { + "type": { + "value": "ParticleEmitter3D::ParticleEmitter3D::SetZ" + }, + "parameters": [ + "Object", + "=", + "Object.Z()", + "" + ] + }, + { + "type": { + "value": "ParticleEmitter3D::ParticleEmitter3D::SetRotationX" + }, + "parameters": [ + "Object", + "=", + "Object.PropertyRotationX()", + "" + ] + }, + { + "type": { + "value": "ParticleEmitter3D::ParticleEmitter3D::SetRotationY" + }, + "parameters": [ + "Object", + "=", + "Object.PropertyRotationY()", + "" + ] + }, + { + "type": { + "value": "ParticleEmitter3D::ParticleEmitter3D::SetRenderMode" + }, + "parameters": [ + "Object", + "=", + "RenderMode", + "" + ] + }, + { + "type": { + "value": "ParticleEmitter3D::ParticleEmitter3D::SetTrailStartLengthMin" + }, + "parameters": [ + "Object", + "=", + "TrailStartLengthMin", + "" + ] + }, + { + "type": { + "value": "ParticleEmitter3D::ParticleEmitter3D::SetTrailStartLengthMax" + }, + "parameters": [ + "Object", + "=", + "TrailStartLengthMax", + "" + ] + }, + { + "type": { + "value": "ParticleEmitter3D::ParticleEmitter3D::SetTrailEndWidthRatio" + }, + "parameters": [ + "Object", + "=", + "TrailEndWidthRatio", + "" + ] + }, + { + "type": { + "value": "ParticleEmitter3D::ParticleEmitter3D::SetStartColor" + }, + "parameters": [ + "Object", + "=", + "Object.StartColor()", + "" + ] + }, + { + "type": { + "value": "ParticleEmitter3D::ParticleEmitter3D::SetEndColor" + }, + "parameters": [ + "Object", + "=", + "Object.EndColor()", + "" + ] + }, + { + "type": { + "value": "ParticleEmitter3D::ParticleEmitter3D::SetStartOpacity" + }, + "parameters": [ + "Object", + "=", + "Object.StartOpacity()", + "" + ] + }, + { + "type": { + "value": "ParticleEmitter3D::ParticleEmitter3D::SetEndOpacity" + }, + "parameters": [ + "Object", + "=", + "Object.EndOpacity()", + "" + ] + }, + { + "type": { + "value": "ParticleEmitter3D::ParticleEmitter3D::SetFlow" + }, + "parameters": [ + "Object", + "=", + "Object.Flow()", + "" + ] + }, + { + "type": { + "value": "ParticleEmitter3D::ParticleEmitter3D::SetStartSizeMin" + }, + "parameters": [ + "Object", + "=", + "Object.StartSizeMin()", + "" + ] + }, + { + "type": { + "value": "ParticleEmitter3D::ParticleEmitter3D::SetStartSizeMax" + }, + "parameters": [ + "Object", + "=", + "Object.StartSizeMax()", + "" + ] + }, + { + "type": { + "value": "ParticleEmitter3D::ParticleEmitter3D::SetEndScale" + }, + "parameters": [ + "Object", + "=", + "Object.EndScale()", + "" + ] + }, + { + "type": { + "value": "ParticleEmitter3D::ParticleEmitter3D::SetStartSpeedMin" + }, + "parameters": [ + "Object", + "=", + "Object.StartSpeedMin()", + "" + ] + }, + { + "type": { + "value": "ParticleEmitter3D::ParticleEmitter3D::SetStartSpeedMax" + }, + "parameters": [ + "Object", + "=", + "Object.StartSpeedMax()", + "" + ] + }, + { + "type": { + "value": "ParticleEmitter3D::ParticleEmitter3D::SetLifespanMin" + }, + "parameters": [ + "Object", + "=", + "Object.LifespanMin()", + "" + ] + }, + { + "type": { + "value": "ParticleEmitter3D::ParticleEmitter3D::SetLifespanMax" + }, + "parameters": [ + "Object", + "=", + "Object.LifespanMax()", + "" + ] + }, + { + "type": { + "value": "ParticleEmitter3D::ParticleEmitter3D::SetDuration" + }, + "parameters": [ + "Object", + "=", + "Object.Duration()", + "" + ] + }, + { + "type": { + "value": "ParticleEmitter3D::ParticleEmitter3D::SetSpayConeAngle" + }, + "parameters": [ + "Object", + "=", + "Object.SpayConeAngle()", + "" + ] + }, + { + "type": { + "value": "ParticleEmitter3D::ParticleEmitter3D::SetGravity" + }, + "parameters": [ + "Object", + "=", + "Object.Gravity()", + "" + ] + }, + { + "type": { + "value": "ParticleEmitter3D::ParticleEmitter3D::SetGravityTop" + }, + "parameters": [ + "Object", + "=", + "Object.GravityTop()", + "" + ] + }, + { + "type": { + "value": "ParticleEmitter3D::ParticleEmitter3D::UpdateImage" + }, + "parameters": [ + "Object", + "" + ] + } + ], + "events": [ + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [ + { + "type": { + "inverted": true, + "value": "BooleanVariable" + }, + "parameters": [ + "IsTrailFollowingLocalOrigin", + "True", + "" + ] + } + ], + "actions": [ + { + "type": { + "value": "ParticleEmitter3D::ParticleEmitter3D::SetIsTrailFollowingLocalOrigin" + }, + "parameters": [ + "Object", + "=", + "TrailStartLengthMax" + ] + } + ] + }, + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [ + { + "type": { + "value": "BooleanVariable" + }, + "parameters": [ + "IsTrailFollowingLocalOrigin", + "True", + "" + ] + } + ], + "actions": [ + { + "type": { + "value": "ParticleEmitter3D::ParticleEmitter3D::SetIsTrailFollowingLocalOrigin" + }, + "parameters": [ + "Object", + "yes", + "TrailStartLengthMax" + ] + } + ] + }, + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [ + { + "type": { + "inverted": true, + "value": "BooleanVariable" + }, + "parameters": [ + "AreParticlesRelative", + "True", + "" + ] + } + ], + "actions": [ + { + "type": { + "value": "ParticleEmitter3D::ParticleEmitter3D::SetAreParticlesRelative" + }, + "parameters": [ + "Object", + "no", + "" + ] + } + ] + }, + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [ + { + "type": { + "value": "BooleanVariable" + }, + "parameters": [ + "AreParticlesRelative", + "True", + "" + ] + } + ], + "actions": [ + { + "type": { + "value": "ParticleEmitter3D::ParticleEmitter3D::SetAreParticlesRelative" + }, + "parameters": [ + "Object", + "yes", + "" + ] + } + ] + }, + { + "type": "BuiltinCommonInstructions::Comment", + "color": { + "b": 109, + "g": 230, + "r": 255, + "textB": 0, + "textG": 0, + "textR": 0 + }, + "comment": "TODO: Blending can't be changed." + }, + { + "disabled": true, + "type": "BuiltinCommonInstructions::Standard", + "conditions": [], + "actions": [ + { + "type": { + "value": "ParticleEmitter3D::ParticleEmitter3D::SetBlending" + }, + "parameters": [ + "Object", + "=", + "Object.Blending()", + "" + ] + } + ] + } + ] + }, + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [], + "actions": [ + { + "type": { + "value": "ParticleEmitter3D::ParticleEmitter3D::UpdateHelper" + }, + "parameters": [ + "Object", + "" + ] + } + ] + } + ], + "parameters": [ + { + "description": "Object", + "name": "Object", + "supplementaryInformation": "ParticleEmitter3D::ParticleEmitter3D", + "type": "object" + } + ], + "objectGroups": [] + }, + { + "fullName": "Update helper", + "functionType": "Action", + "name": "UpdateHelper", + "private": true, + "sentence": "Update graphical helper of _PARAM0_", + "events": [ + { + "type": "BuiltinCommonInstructions::JsCode", + "inlineCode": [ + "const game = runtimeScene.getGame();", + "if (game.isInGameEdition && game.isInGameEdition()) {", + " const particleEmitterHelper = objects[0].get3DRendererObject();", + " particleEmitterHelper.update();", + "}" + ], + "parameterObjects": "Object", + "useStrict": true, + "eventsSheetExpanded": false + } + ], + "parameters": [ + { + "description": "Object", + "name": "Object", + "supplementaryInformation": "ParticleEmitter3D::ParticleEmitter3D", + "type": "object" + } + ], + "objectGroups": [] + }, + { + "fullName": "Update particle image", + "functionType": "Action", + "name": "UpdateImage", + "private": true, + "sentence": "Update particle image of _PARAM0_", + "events": [ + { + "type": "BuiltinCommonInstructions::JsCode", + "inlineCode": [ + "/** @type {gdjs.CustomRuntimeObject3D} */\r", + "const object = objects[0];\r", + "/** @type {SpriteObjectDataType} */\r", + "const particleSpriteData = object._instanceContainer._objects.get(\"Particle\");\r", + "/** @type {gdjs.SpriteAnimationData} */\r", + "const animation = particleSpriteData.animations[0];\r", + "const animationFrame = animation ? animation.directions[0].sprites[0] : null;\r", + "const resourceName = animationFrame ? animationFrame.image : '';\r", + "object.__particleEmitterAdapter.setImage(resourceName);" + ], + "parameterObjects": "Object", + "useStrict": true, + "eventsSheetExpanded": true + } + ], + "parameters": [ + { + "description": "Object", + "name": "Object", + "supplementaryInformation": "ParticleEmitter3D::ParticleEmitter3D", + "type": "object" + } + ], + "objectGroups": [] + }, + { + "fullName": "", + "functionType": "Action", + "name": "doStepPostEvents", + "sentence": "", + "events": [ + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [ + { + "type": { + "value": "ParticleEmitter3D::ParticleEmitter3D::HasEnded" + }, + "parameters": [ + "Object", + "" + ] + } + ], + "actions": [ + { + "type": { + "value": "ParticleEmitter3D::ParticleEmitter3D::Delete" + }, + "parameters": [ + "Object", + "" + ] + } + ] + }, + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [ + { + "type": { + "inverted": true, + "value": "ParticleEmitter3D::ParticleEmitter3D::HasEnded" + }, + "parameters": [ + "Object", + "" + ] + } + ], + "actions": [ + { + "type": { + "value": "ParticleEmitter3D::ParticleEmitter3D::RegisterInLayer" + }, + "parameters": [ + "Object", + "" + ] + } + ], + "events": [ + { + "type": "BuiltinCommonInstructions::JsCode", + "inlineCode": [ + "/** @type {gdjs.CustomRuntimeObject3D} */", + "const object = objects[0];", + "// Here runtimeScene is the gdjs.CustomRuntimeObjectInstanceContainer inside the custom object.", + "const gameScene = object.getRuntimeScene();", + "", + "// Update batch system after having moved every emitter.", + "// See onScenePreEvents", + "gameScene.__particleEmmiter3DExtension.emittersStepped++;", + "if (gameScene.__particleEmmiter3DExtension.emittersStepped === gameScene.__particleEmmiter3DExtension.emittersCount) {", + " gameScene.__particleEmmiter3DExtension.layerNames = gameScene.__particleEmmiter3DExtension.layerNames || [];", + " const layerNames = gameScene.__particleEmmiter3DExtension.layerNames;", + " gameScene.getAllLayerNames(layerNames);", + " for (const layerName of layerNames) {", + " const layer = gameScene.getLayer(layerName);", + " if (layer.__particleEmmiter3DExtension) {", + " const timeDelta = layer.getElapsedTime() / 1000;", + " if (timeDelta > 0) {", + " layer.__particleEmmiter3DExtension.batchSystem.update(timeDelta);", + " }", + " }", + " }", + "}" + ], + "parameterObjects": "Object", + "useStrict": true, + "eventsSheetExpanded": true + } + ] + } + ], + "parameters": [ + { + "description": "Object", + "name": "Object", + "supplementaryInformation": "ParticleEmitter3D::ParticleEmitter3D", + "type": "object" + } + ], + "objectGroups": [] + }, + { + "fullName": "Register in layer", + "functionType": "Action", + "name": "RegisterInLayer", + "private": true, + "sentence": "Register _PARAM0_ in layer", + "events": [ + { + "type": "BuiltinCommonInstructions::JsCode", + "inlineCode": [ + "/** @type {gdjs.CustomRuntimeObject} */", + "const object = objects[0];", + "", + "// TODO Handle layer changes?", + "if (object.__emitterLayerName === undefined) {", + " // Here runtimeScene is the gdjs.CustomRuntimeObjectInstanceContainer inside the custom object.", + " const gameScene = object.getRuntimeScene();", + "", + " let rootObject = object;", + " while (rootObject.getInstanceContainer().getOwner) {", + " rootObject = rootObject.getInstanceContainer().getOwner();", + " }", + "", + " const layer = gameScene.getLayer(rootObject.getLayer());", + " layer.__particleEmmiter3DExtension = layer.__particleEmmiter3DExtension || {};", + " if (!layer.__particleEmmiter3DExtension.batchSystem) {", + " const batchSystem = new gdjs.__particleEmmiter3DExtension.BatchedRenderer();", + " batchSystem.scale.y *= -1;", + " const threeScene = layer.getRenderer().getThreeScene();", + " if (threeScene) {", + " threeScene.add(batchSystem);", + " }", + " layer.__particleEmmiter3DExtension.batchSystem = batchSystem;", + " }", + " layer.__particleEmmiter3DExtension.batchSystem.addSystem(object.__particleSystem);", + " object.__emitterLayerName = layer.getName();", + "}" + ], + "parameterObjects": "Object", + "useStrict": true, + "eventsSheetExpanded": true + } + ], + "parameters": [ + { + "description": "Object", + "name": "Object", + "supplementaryInformation": "ParticleEmitter3D::ParticleEmitter3D", + "type": "object" + } + ], + "objectGroups": [] + }, + { + "description": "Delete itself", + "fullName": "Delete itself", + "functionType": "Action", + "name": "Delete", + "private": true, + "sentence": "Delete _PARAM0_", + "events": [ + { + "type": "BuiltinCommonInstructions::JsCode", + "inlineCode": [ + "const object = objects[0];", + "", + "object.deleteFromScene(object.getParent());", + "" + ], + "parameterObjects": "Object", + "useStrict": true, + "eventsSheetExpanded": true + } + ], + "parameters": [ + { + "description": "Object", + "name": "Object", + "supplementaryInformation": "ParticleEmitter3D::ParticleEmitter3D", + "type": "object" + } + ], + "objectGroups": [] + }, + { + "description": "Check that emission has ended and no particle is alive anymore.", + "fullName": "Emission has ended", + "functionType": "Condition", + "name": "HasEnded", + "sentence": "Emission from _PARAM0_ has ended", + "events": [ + { + "type": "BuiltinCommonInstructions::JsCode", + "inlineCode": [ + "const object = objects[0];\r", + "\r", + "eventsFunctionContext.returnValue =\r", + " object._getShouldAutodestruct()\r", + " && object.__particleSystem.emitEnded\r", + " && object.__particleSystem.particleNum === 0;\r", + "" + ], + "parameterObjects": "Object", + "useStrict": true, + "eventsSheetExpanded": true + } + ], + "parameters": [ + { + "description": "Object", + "name": "Object", + "supplementaryInformation": "ParticleEmitter3D::ParticleEmitter3D", + "type": "object" + } + ], + "objectGroups": [] + }, + { + "description": "Restart particule emission from the beginning.", + "fullName": "Restart", + "functionType": "Action", + "name": "Restart", + "sentence": "Restart particule emission from _PARAM0_", + "events": [ + { + "type": "BuiltinCommonInstructions::JsCode", + "inlineCode": [ + "/** @type {gdjs.CustomRuntimeObject} */\r", + "const object = objects[0];\r", + "\r", + "object.__particleSystem.restart();\r", + "" + ], + "parameterObjects": "Object", + "useStrict": true, + "eventsSheetExpanded": false + } + ], + "parameters": [ + { + "description": "Object", + "name": "Object", + "supplementaryInformation": "ParticleEmitter3D::ParticleEmitter3D", + "type": "object" + } + ], + "objectGroups": [] + }, + { + "description": "the Z position of the emitter.", + "fullName": "Z elevaltion (deprecated)", + "functionType": "ExpressionAndCondition", + "group": "Position", + "name": "Z", + "private": true, + "sentence": "the Z position", + "events": [ + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [], + "actions": [ + { + "type": { + "value": "SetReturnNumber" + }, + "parameters": [ + "Object.PropertyZ()" + ] + } + ] + } + ], + "expressionType": { + "type": "expression" + }, + "parameters": [ + { + "description": "Object", + "name": "Object", + "supplementaryInformation": "ParticleEmitter3D::ParticleEmitter3D", + "type": "object" + } + ], + "objectGroups": [] + }, + { + "fullName": "", + "functionType": "ActionWithOperator", + "getterName": "Z", + "group": "Position", + "name": "SetZ", + "private": true, + "sentence": "", + "events": [ + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [], + "actions": [ + { + "type": { + "value": "SetNumberVariable" + }, + "parameters": [ + "Z", + "=", + "Value" + ] + } + ] + } + ], + "parameters": [ + { + "description": "Object", + "name": "Object", + "supplementaryInformation": "ParticleEmitter3D::ParticleEmitter3D", + "type": "object" + } + ], + "objectGroups": [] + }, + { + "description": "the rotation on X axis of the emitter.", + "fullName": "Rotation on X axis (deprecated)", + "functionType": "ExpressionAndCondition", + "group": "Angle", + "name": "RotationX", + "private": true, + "sentence": "the rotation on X axis", + "events": [ + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [], + "actions": [ + { + "type": { + "value": "SetReturnNumber" + }, + "parameters": [ + "Object.PropertyRotationX()" + ] + } + ] + } + ], + "expressionType": { + "type": "expression" + }, + "parameters": [ + { + "description": "Object", + "name": "Object", + "supplementaryInformation": "ParticleEmitter3D::ParticleEmitter3D", + "type": "object" + } + ], + "objectGroups": [] + }, + { + "fullName": "", + "functionType": "ActionWithOperator", + "getterName": "RotationX", + "group": "Angle", + "name": "SetRotationX", + "private": true, + "sentence": "", + "events": [ + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [], + "actions": [ + { + "type": { + "value": "SetNumberVariable" + }, + "parameters": [ + "RotationX", + "=", + "Value" + ] + } + ] + }, + { + "type": "BuiltinCommonInstructions::JsCode", + "inlineCode": [ + "const object = objects[0];\r", + "\r", + "object.__particleSystem.emitter.rotation.x = eventsFunctionContext.getArgument(\"Value\") * Math.PI / 180;" + ], + "parameterObjects": "Object", + "useStrict": true, + "eventsSheetExpanded": false + } + ], + "parameters": [ + { + "description": "Object", + "name": "Object", + "supplementaryInformation": "ParticleEmitter3D::ParticleEmitter3D", + "type": "object" + } + ], + "objectGroups": [] + }, + { + "description": "the rotation on Y axis of the emitter.", + "fullName": "Rotation on Y axis (deprecated)", + "functionType": "ExpressionAndCondition", + "group": "Angle", + "name": "RotationY", + "private": true, + "sentence": "the rotation on Y axis", + "events": [ + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [], + "actions": [ + { + "type": { + "value": "SetReturnNumber" + }, + "parameters": [ + "Object.PropertyRotationY()" + ] + } + ] + } + ], + "expressionType": { + "type": "expression" + }, + "parameters": [ + { + "description": "Object", + "name": "Object", + "supplementaryInformation": "ParticleEmitter3D::ParticleEmitter3D", + "type": "object" + } + ], + "objectGroups": [] + }, + { + "fullName": "", + "functionType": "ActionWithOperator", + "getterName": "RotationY", + "group": "Angle", + "name": "SetRotationY", + "private": true, + "sentence": "", + "events": [ + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [], + "actions": [ + { + "type": { + "value": "SetNumberVariable" + }, + "parameters": [ + "RotationY", + "=", + "Value" + ] + } + ] + }, + { + "type": "BuiltinCommonInstructions::JsCode", + "inlineCode": [ + "const object = objects[0];\r", + "\r", + "object.__particleSystem.emitter.rotation.y = (90 + eventsFunctionContext.getArgument(\"Value\")) * Math.PI / 180;" + ], + "parameterObjects": "Object", + "useStrict": true, + "eventsSheetExpanded": false + } + ], + "parameters": [ + { + "description": "Object", + "name": "Object", + "supplementaryInformation": "ParticleEmitter3D::ParticleEmitter3D", + "type": "object" + } + ], + "objectGroups": [] + }, + { + "description": "the start color of the object.", + "fullName": "Start color", + "functionType": "ExpressionAndCondition", + "group": "3D particle emitter color configuration", + "name": "StartColor", + "sentence": "the start color", + "events": [ + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [], + "actions": [ + { + "type": { + "value": "SetReturnString" + }, + "parameters": [ + "StartColor" + ] + } + ] + } + ], + "expressionType": { + "type": "color" + }, + "parameters": [ + { + "description": "Object", + "name": "Object", + "supplementaryInformation": "ParticleEmitter3D::ParticleEmitter3D", + "type": "object" + } + ], + "objectGroups": [] + }, + { + "fullName": "", + "functionType": "ActionWithOperator", + "getterName": "StartColor", + "group": "3D particle emitter color configuration", + "name": "SetStartColor", + "sentence": "", + "events": [ + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [], + "actions": [ + { + "type": { + "value": "SetStringVariable" + }, + "parameters": [ + "StartColor", + "=", + "Value" + ] + } + ] + }, + { + "type": "BuiltinCommonInstructions::JsCode", + "inlineCode": [ + "const object = objects[0];\r", + "const startColor = eventsFunctionContext.getArgument(\"Value\");\r", + "\r", + "object.__particleEmitterAdapter.setStartColor(startColor);" + ], + "parameterObjects": "Object", + "useStrict": true, + "eventsSheetExpanded": false + } + ], + "parameters": [ + { + "description": "Object", + "name": "Object", + "supplementaryInformation": "ParticleEmitter3D::ParticleEmitter3D", + "type": "object" + } + ], + "objectGroups": [] + }, + { + "description": "the end color of the object.", + "fullName": "End color", + "functionType": "ExpressionAndCondition", + "group": "3D particle emitter color configuration", + "name": "EndColor", + "sentence": "the end color", + "events": [ + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [], + "actions": [ + { + "type": { + "value": "SetReturnString" + }, + "parameters": [ + "EndColor" + ] + } + ] + } + ], + "expressionType": { + "type": "color" + }, + "parameters": [ + { + "description": "Object", + "name": "Object", + "supplementaryInformation": "ParticleEmitter3D::ParticleEmitter3D", + "type": "object" + } + ], + "objectGroups": [] + }, + { + "fullName": "", + "functionType": "ActionWithOperator", + "getterName": "EndColor", + "group": "3D particle emitter color configuration", + "name": "SetEndColor", + "sentence": "", + "events": [ + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [], + "actions": [ + { + "type": { + "value": "SetStringVariable" + }, + "parameters": [ + "EndColor", + "=", + "Value" + ] + } + ] + }, + { + "type": "BuiltinCommonInstructions::JsCode", + "inlineCode": [ + "const object = objects[0];\r", + "const endColor = eventsFunctionContext.getArgument(\"Value\");\r", + "\r", + "object.__particleEmitterAdapter.setEndColor(endColor);" + ], + "parameterObjects": "Object", + "useStrict": true, + "eventsSheetExpanded": false + } + ], + "parameters": [ + { + "description": "Object", + "name": "Object", + "supplementaryInformation": "ParticleEmitter3D::ParticleEmitter3D", + "type": "object" + } + ], + "objectGroups": [] + }, + { + "description": "the start opacity of the object.", + "fullName": "Start opacity", + "functionType": "ExpressionAndCondition", + "group": "3D particle emitter color configuration", + "name": "StartOpacity", + "sentence": "the start opacity", + "events": [ + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [], + "actions": [ + { + "type": { + "value": "SetReturnNumber" + }, + "parameters": [ + "StartOpacity" + ] + } + ] + } + ], + "expressionType": { + "type": "expression" + }, + "parameters": [ + { + "description": "Object", + "name": "Object", + "supplementaryInformation": "ParticleEmitter3D::ParticleEmitter3D", + "type": "object" + } + ], + "objectGroups": [] + }, + { + "fullName": "", + "functionType": "ActionWithOperator", + "getterName": "StartOpacity", + "group": "3D particle emitter color configuration", + "name": "SetStartOpacity", + "sentence": "", + "events": [ + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [], + "actions": [ + { + "type": { + "value": "SetNumberVariable" + }, + "parameters": [ + "StartOpacity", + "=", + "Value" + ] + } + ] + }, + { + "type": "BuiltinCommonInstructions::JsCode", + "inlineCode": [ + "const object = objects[0];\r", + "const startOpacity = eventsFunctionContext.getArgument(\"Value\");\r", + "\r", + "object.__particleEmitterAdapter.setStartOpacity(startOpacity);" + ], + "parameterObjects": "Object", + "useStrict": true, + "eventsSheetExpanded": false + } + ], + "parameters": [ + { + "description": "Object", + "name": "Object", + "supplementaryInformation": "ParticleEmitter3D::ParticleEmitter3D", + "type": "object" + } + ], + "objectGroups": [] + }, + { + "description": "the end opacity of the object.", + "fullName": "End opacity", + "functionType": "ExpressionAndCondition", + "group": "3D particle emitter color configuration", + "name": "EndOpacity", + "sentence": "the end opacity", + "events": [ + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [], + "actions": [ + { + "type": { + "value": "SetReturnNumber" + }, + "parameters": [ + "EndOpacity" + ] + } + ] + } + ], + "expressionType": { + "type": "expression" + }, + "parameters": [ + { + "description": "Object", + "name": "Object", + "supplementaryInformation": "ParticleEmitter3D::ParticleEmitter3D", + "type": "object" + } + ], + "objectGroups": [] + }, + { + "fullName": "", + "functionType": "ActionWithOperator", + "getterName": "EndOpacity", + "group": "3D particle emitter color configuration", + "name": "SetEndOpacity", + "sentence": "", + "events": [ + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [], + "actions": [ + { + "type": { + "value": "SetNumberVariable" + }, + "parameters": [ + "EndOpacity", + "=", + "Value" + ] + } + ] + }, + { + "type": "BuiltinCommonInstructions::JsCode", + "inlineCode": [ + "const object = objects[0];\r", + "const endOpacity = eventsFunctionContext.getArgument(\"Value\");\r", + "\r", + "object.__particleEmitterAdapter.setEndOpacity(endOpacity);" + ], + "parameterObjects": "Object", + "useStrict": true, + "eventsSheetExpanded": false + } + ], + "parameters": [ + { + "description": "Object", + "name": "Object", + "supplementaryInformation": "ParticleEmitter3D::ParticleEmitter3D", + "type": "object" + } + ], + "objectGroups": [] + }, + { + "description": "the flow of particles of the object (particles per second).", + "fullName": "Flow of particles", + "functionType": "ExpressionAndCondition", + "group": "3D particle emitter configuration", + "name": "Flow", + "sentence": "the flow of particles", + "events": [ + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [], + "actions": [ + { + "type": { + "value": "SetReturnNumber" + }, + "parameters": [ + "Flow" + ] + } + ] + } + ], + "expressionType": { + "type": "expression" + }, + "parameters": [ + { + "description": "Object", + "name": "Object", + "supplementaryInformation": "ParticleEmitter3D::ParticleEmitter3D", + "type": "object" + } + ], + "objectGroups": [] + }, + { + "fullName": "", + "functionType": "ActionWithOperator", + "getterName": "Flow", + "group": "3D particle emitter configuration", + "name": "SetFlow", + "sentence": "", + "events": [ + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [], + "actions": [ + { + "type": { + "value": "SetNumberVariable" + }, + "parameters": [ + "Flow", + "=", + "Value" + ] + } + ] + }, + { + "type": "BuiltinCommonInstructions::JsCode", + "inlineCode": [ + "const object = objects[0];\r", + "const flow = eventsFunctionContext.getArgument(\"Value\");\r", + "\r", + "object.__particleEmitterAdapter.setFlow(flow);\r", + "" + ], + "parameterObjects": "Object", + "useStrict": true, + "eventsSheetExpanded": false + } + ], + "parameters": [ + { + "description": "Object", + "name": "Object", + "supplementaryInformation": "ParticleEmitter3D::ParticleEmitter3D", + "type": "object" + } + ], + "objectGroups": [] + }, + { + "description": "the start min size of the object.", + "fullName": "Start min size", + "functionType": "ExpressionAndCondition", + "group": "3D particle emitter configuration", + "name": "StartSizeMin", + "sentence": "the start min size", + "events": [ + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [], + "actions": [ + { + "type": { + "value": "SetReturnNumber" + }, + "parameters": [ + "StartSizeMin" + ] + } + ] + } + ], + "expressionType": { + "type": "expression" + }, + "parameters": [ + { + "description": "Object", + "name": "Object", + "supplementaryInformation": "ParticleEmitter3D::ParticleEmitter3D", + "type": "object" + } + ], + "objectGroups": [] + }, + { + "fullName": "", + "functionType": "ActionWithOperator", + "getterName": "StartSizeMin", + "group": "3D particle emitter configuration", + "name": "SetStartSizeMin", + "sentence": "", + "events": [ + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [], + "actions": [ + { + "type": { + "value": "SetNumberVariable" + }, + "parameters": [ + "StartSizeMin", + "=", + "Value" + ] + } + ] + }, + { + "type": "BuiltinCommonInstructions::JsCode", + "inlineCode": [ + "const object = objects[0];\r", + "const startMinSize = eventsFunctionContext.getArgument(\"Value\");\r", + "\r", + "object.__particleEmitterAdapter.setStartMinSize(startMinSize);" + ], + "parameterObjects": "Object", + "useStrict": true, + "eventsSheetExpanded": false + } + ], + "parameters": [ + { + "description": "Object", + "name": "Object", + "supplementaryInformation": "ParticleEmitter3D::ParticleEmitter3D", + "type": "object" + } + ], + "objectGroups": [] + }, + { + "description": "the start max size of the object.", + "fullName": "Start max size", + "functionType": "ExpressionAndCondition", + "group": "3D particle emitter configuration", + "name": "StartSizeMax", + "sentence": "the start max size", + "events": [ + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [], + "actions": [ + { + "type": { + "value": "SetReturnNumber" + }, + "parameters": [ + "StartSizeMax" + ] + } + ] + } + ], + "expressionType": { + "type": "expression" + }, + "parameters": [ + { + "description": "Object", + "name": "Object", + "supplementaryInformation": "ParticleEmitter3D::ParticleEmitter3D", + "type": "object" + } + ], + "objectGroups": [] + }, + { + "fullName": "", + "functionType": "ActionWithOperator", + "getterName": "StartSizeMax", + "group": "3D particle emitter configuration", + "name": "SetStartSizeMax", + "sentence": "", + "events": [ + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [], + "actions": [ + { + "type": { + "value": "SetNumberVariable" + }, + "parameters": [ + "StartSizeMax", + "=", + "Value" + ] + } + ] + }, + { + "type": "BuiltinCommonInstructions::JsCode", + "inlineCode": [ + "const object = objects[0];\r", + "const startMaxSize = eventsFunctionContext.getArgument(\"Value\");\r", + "\r", + "object.__particleEmitterAdapter.setStartMaxSize(startMaxSize);\r", + "" + ], + "parameterObjects": "Object", + "useStrict": true, + "eventsSheetExpanded": false + } + ], + "parameters": [ + { + "description": "Object", + "name": "Object", + "supplementaryInformation": "ParticleEmitter3D::ParticleEmitter3D", + "type": "object" + } + ], + "objectGroups": [] + }, + { + "description": "the end scale of the object.", + "fullName": "End scale", + "functionType": "ExpressionAndCondition", + "group": "3D particle emitter configuration", + "name": "EndScale", + "sentence": "the end scale", + "events": [ + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [], + "actions": [ + { + "type": { + "value": "SetReturnNumber" + }, + "parameters": [ + "EndScale" + ] + } + ] + } + ], + "expressionType": { + "type": "expression" + }, + "parameters": [ + { + "description": "Object", + "name": "Object", + "supplementaryInformation": "ParticleEmitter3D::ParticleEmitter3D", + "type": "object" + } + ], + "objectGroups": [] + }, + { + "fullName": "", + "functionType": "ActionWithOperator", + "getterName": "EndScale", + "group": "3D particle emitter configuration", + "name": "SetEndScale", + "sentence": "", + "events": [ + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [], + "actions": [ + { + "type": { + "value": "SetNumberVariable" + }, + "parameters": [ + "EndScale", + "=", + "Value" + ] + } + ] + }, + { + "type": "BuiltinCommonInstructions::JsCode", + "inlineCode": [ + "const object = objects[0];\r", + "const endScale = eventsFunctionContext.getArgument(\"Value\");\r", + "\r", + "object.__particleEmitterAdapter.setEndScale(endScale);\r", + "" + ], + "parameterObjects": "Object", + "useStrict": true, + "eventsSheetExpanded": false + } + ], + "parameters": [ + { + "description": "Object", + "name": "Object", + "supplementaryInformation": "ParticleEmitter3D::ParticleEmitter3D", + "type": "object" + } + ], + "objectGroups": [] + }, + { + "description": "the min start speed of the object.", + "fullName": "Min start speed", + "functionType": "ExpressionAndCondition", + "group": "3D particle emitter speed configuration", + "name": "StartSpeedMin", + "sentence": "the min start speed", + "events": [ + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [], + "actions": [ + { + "type": { + "value": "SetReturnNumber" + }, + "parameters": [ + "StartSpeedMin" + ] + } + ] + } + ], + "expressionType": { + "type": "expression" + }, + "parameters": [ + { + "description": "Object", + "name": "Object", + "supplementaryInformation": "ParticleEmitter3D::ParticleEmitter3D", + "type": "object" + } + ], + "objectGroups": [] + }, + { + "fullName": "", + "functionType": "ActionWithOperator", + "getterName": "StartSpeedMin", + "group": "3D particle emitter speed configuration", + "name": "SetStartSpeedMin", + "sentence": "", + "events": [ + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [], + "actions": [ + { + "type": { + "value": "SetNumberVariable" + }, + "parameters": [ + "StartSpeedMin", + "=", + "Value" + ] + } + ] + }, + { + "type": "BuiltinCommonInstructions::JsCode", + "inlineCode": [ + "const object = objects[0];\r", + "const startSpeedMin = eventsFunctionContext.getArgument(\"Value\");\r", + "\r", + "object.__particleEmitterAdapter.setStartSpeedMin(startSpeedMin);" + ], + "parameterObjects": "Object", + "useStrict": true, + "eventsSheetExpanded": false + } + ], + "parameters": [ + { + "description": "Object", + "name": "Object", + "supplementaryInformation": "ParticleEmitter3D::ParticleEmitter3D", + "type": "object" + } + ], + "objectGroups": [] + }, + { + "description": "the max start speed of the object.", + "fullName": "Max start speed", + "functionType": "ExpressionAndCondition", + "group": "3D particle emitter speed configuration", + "name": "StartSpeedMax", + "sentence": "the max start speed", + "events": [ + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [], + "actions": [ + { + "type": { + "value": "SetReturnNumber" + }, + "parameters": [ + "StartSpeedMax" + ] + } + ] + } + ], + "expressionType": { + "type": "expression" + }, + "parameters": [ + { + "description": "Object", + "name": "Object", + "supplementaryInformation": "ParticleEmitter3D::ParticleEmitter3D", + "type": "object" + } + ], + "objectGroups": [] + }, + { + "fullName": "", + "functionType": "ActionWithOperator", + "getterName": "StartSpeedMax", + "group": "3D particle emitter speed configuration", + "name": "SetStartSpeedMax", + "sentence": "", + "events": [ + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [], + "actions": [ + { + "type": { + "value": "SetNumberVariable" + }, + "parameters": [ + "StartSpeedMax", + "=", + "Value" + ] + } + ] + }, + { + "type": "BuiltinCommonInstructions::JsCode", + "inlineCode": [ + "const object = objects[0];\r", + "const startSpeedMax = eventsFunctionContext.getArgument(\"Value\");\r", + "\r", + "object.__particleEmitterAdapter.setStartSpeedMax(startSpeedMax);" + ], + "parameterObjects": "Object", + "useStrict": true, + "eventsSheetExpanded": false + } + ], + "parameters": [ + { + "description": "Object", + "name": "Object", + "supplementaryInformation": "ParticleEmitter3D::ParticleEmitter3D", + "type": "object" + } + ], + "objectGroups": [] + }, + { + "description": "the min lifespan of the object.", + "fullName": "Min lifespan", + "functionType": "ExpressionAndCondition", + "group": "3D particle emitter configuration", + "name": "LifespanMin", + "sentence": "the min lifespan", + "events": [ + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [], + "actions": [ + { + "type": { + "value": "SetReturnNumber" + }, + "parameters": [ + "LifespanMin" + ] + } + ] + } + ], + "expressionType": { + "type": "expression" + }, + "parameters": [ + { + "description": "Object", + "name": "Object", + "supplementaryInformation": "ParticleEmitter3D::ParticleEmitter3D", + "type": "object" + } + ], + "objectGroups": [] + }, + { + "fullName": "", + "functionType": "ActionWithOperator", + "getterName": "LifespanMin", + "group": "3D particle emitter configuration", + "name": "SetLifespanMin", + "sentence": "", + "events": [ + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [], + "actions": [ + { + "type": { + "value": "SetNumberVariable" + }, + "parameters": [ + "LifespanMin", + "=", + "Value" + ] + } + ] + }, + { + "type": "BuiltinCommonInstructions::JsCode", + "inlineCode": [ + "const object = objects[0];\r", + "const lifespanMin = eventsFunctionContext.getArgument(\"Value\");\r", + "\r", + "object.__particleEmitterAdapter.setLifespanMin(lifespanMin);\r", + "" + ], + "parameterObjects": "Object", + "useStrict": true, + "eventsSheetExpanded": false + } + ], + "parameters": [ + { + "description": "Object", + "name": "Object", + "supplementaryInformation": "ParticleEmitter3D::ParticleEmitter3D", + "type": "object" + } + ], + "objectGroups": [] + }, + { + "description": "the max lifespan of the object.", + "fullName": "Max lifespan", + "functionType": "ExpressionAndCondition", + "group": "3D particle emitter configuration", + "name": "LifespanMax", + "sentence": "the max lifespan", + "events": [ + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [], + "actions": [ + { + "type": { + "value": "SetReturnNumber" + }, + "parameters": [ + "LifespanMax" + ] + } + ] + } + ], + "expressionType": { + "type": "expression" + }, + "parameters": [ + { + "description": "Object", + "name": "Object", + "supplementaryInformation": "ParticleEmitter3D::ParticleEmitter3D", + "type": "object" + } + ], + "objectGroups": [] + }, + { + "fullName": "", + "functionType": "ActionWithOperator", + "getterName": "LifespanMax", + "group": "3D particle emitter configuration", + "name": "SetLifespanMax", + "sentence": "", + "events": [ + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [], + "actions": [ + { + "type": { + "value": "SetNumberVariable" + }, + "parameters": [ + "LifespanMax", + "=", + "Value" + ] + } + ] + }, + { + "type": "BuiltinCommonInstructions::JsCode", + "inlineCode": [ + "const object = objects[0];\r", + "const lifespanMax = eventsFunctionContext.getArgument(\"Value\");\r", + "\r", + "object.__particleEmitterAdapter.setLifespanMax(lifespanMax);\r", + "" + ], + "parameterObjects": "Object", + "useStrict": true, + "eventsSheetExpanded": false + } + ], + "parameters": [ + { + "description": "Object", + "name": "Object", + "supplementaryInformation": "ParticleEmitter3D::ParticleEmitter3D", + "type": "object" + } + ], + "objectGroups": [] + }, + { + "description": "the emission duration of the object.", + "fullName": "Emission duration", + "functionType": "ExpressionAndCondition", + "group": "3D particle emitter configuration", + "name": "Duration", + "sentence": "the emission duration", + "events": [ + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [], + "actions": [ + { + "type": { + "value": "SetReturnNumber" + }, + "parameters": [ + "Duration" + ] + } + ] + } + ], + "expressionType": { + "type": "expression" + }, + "parameters": [ + { + "description": "Object", + "name": "Object", + "supplementaryInformation": "ParticleEmitter3D::ParticleEmitter3D", + "type": "object" + } + ], + "objectGroups": [] + }, + { + "fullName": "", + "functionType": "ActionWithOperator", + "getterName": "Duration", + "group": "3D particle emitter configuration", + "name": "SetDuration", + "sentence": "", + "events": [ + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [], + "actions": [ + { + "type": { + "value": "SetNumberVariable" + }, + "parameters": [ + "Duration", + "=", + "Value" + ] + } + ] + }, + { + "type": "BuiltinCommonInstructions::JsCode", + "inlineCode": [ + "const object = objects[0];\r", + "const duration = eventsFunctionContext.getArgument(\"Value\");\r", + "\r", + "object.__particleEmitterAdapter.setDuration(duration);\r", + "" + ], + "parameterObjects": "Object", + "useStrict": true, + "eventsSheetExpanded": false + } + ], + "parameters": [ + { + "description": "Object", + "name": "Object", + "supplementaryInformation": "ParticleEmitter3D::ParticleEmitter3D", + "type": "object" + } + ], + "objectGroups": [] + }, + { + "description": "Check if particles move with the emitter.", + "fullName": "Particles move with the emitter", + "functionType": "Condition", + "group": "3D particle emitter speed configuration", + "name": "AreParticlesRelative", + "sentence": "_PARAM0_ particles move with the emitter", + "events": [ + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [ + { + "type": { + "value": "BooleanVariable" + }, + "parameters": [ + "AreParticlesRelative", + "True", + "" + ] + } + ], + "actions": [ + { + "type": { + "value": "SetReturnBoolean" + }, + "parameters": [ + "True" + ] + } + ] + } + ], + "parameters": [ + { + "description": "Object", + "name": "Object", + "supplementaryInformation": "ParticleEmitter3D::ParticleEmitter3D", + "type": "object" + } + ], + "objectGroups": [] + }, + { + "description": "Change if particles move with the emitter.", + "fullName": "Particles move with the emitter", + "functionType": "Action", + "group": "3D particle emitter speed configuration", + "name": "SetAreParticlesRelative", + "sentence": "_PARAM0_ particles move with the emitter: _PARAM1_", + "events": [ + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [ + { + "type": { + "inverted": true, + "value": "BooleanVariable" + }, + "parameters": [ + "Value", + "True", + "" + ] + } + ], + "actions": [ + { + "type": { + "value": "SetBooleanVariable" + }, + "parameters": [ + "AreParticlesRelative", + "False", + "" + ] + } + ] + }, + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [ + { + "type": { + "value": "BooleanVariable" + }, + "parameters": [ + "Value", + "True", + "" + ] + } + ], + "actions": [ + { + "type": { + "value": "SetBooleanVariable" + }, + "parameters": [ + "AreParticlesRelative", + "True", + "" + ] + } + ] + }, + { + "type": "BuiltinCommonInstructions::JsCode", + "inlineCode": [ + "const object = objects[0];\r", + "const areParticlesRelative = eventsFunctionContext.getArgument(\"Value\");\r", + "\r", + "object.__particleEmitterAdapter.setParticlesRelative(areParticlesRelative);\r", + "" + ], + "parameterObjects": "Object", + "useStrict": true, + "eventsSheetExpanded": false + } + ], + "parameters": [ + { + "description": "Object", + "name": "Object", + "supplementaryInformation": "ParticleEmitter3D::ParticleEmitter3D", + "type": "object" + }, + { + "defaultValue": "yes", + "description": "AreParticlesRelative", + "name": "Value", + "optional": true, + "type": "yesorno" + } + ], + "objectGroups": [] + }, + { + "description": "the spay cone angle of the object.", + "fullName": "Spay cone angle", + "functionType": "ExpressionAndCondition", + "group": "3D particle emitter configuration", + "name": "SpayConeAngle", + "sentence": "the spay cone angle", + "events": [ + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [], + "actions": [ + { + "type": { + "value": "SetReturnNumber" + }, + "parameters": [ + "SpayConeAngle" + ] + } + ] + } + ], + "expressionType": { + "type": "expression" + }, + "parameters": [ + { + "description": "Object", + "name": "Object", + "supplementaryInformation": "ParticleEmitter3D::ParticleEmitter3D", + "type": "object" + } + ], + "objectGroups": [] + }, + { + "fullName": "", + "functionType": "ActionWithOperator", + "getterName": "SpayConeAngle", + "group": "3D particle emitter configuration", + "name": "SetSpayConeAngle", + "sentence": "", + "events": [ + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [], + "actions": [ + { + "type": { + "value": "SetNumberVariable" + }, + "parameters": [ + "SpayConeAngle", + "=", + "Value" + ] + } + ] + }, + { + "type": "BuiltinCommonInstructions::JsCode", + "inlineCode": [ + "const object = objects[0];\r", + "const sprayConeAngle = eventsFunctionContext.getArgument(\"Value\");\r", + "\r", + "object.__particleEmitterAdapter.setSprayConeAngle(sprayConeAngle);\r", + "" + ], + "parameterObjects": "Object", + "useStrict": true, + "eventsSheetExpanded": true + } + ], + "parameters": [ + { + "description": "Object", + "name": "Object", + "supplementaryInformation": "ParticleEmitter3D::ParticleEmitter3D", + "type": "object" + } + ], + "objectGroups": [] + }, + { + "description": "the blending of the object.", + "fullName": "Blending", + "functionType": "ExpressionAndCondition", + "group": "3D particle emitter color configuration", + "name": "Blending", + "sentence": "the blending", + "events": [ + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [], + "actions": [ + { + "type": { + "value": "SetReturnString" + }, + "parameters": [ + "Blending" + ] + } + ] + } + ], + "expressionType": { + "supplementaryInformation": "[\"Normal\",\"Additive\",\"Substractive\",\"Multiply\",\"None\"]", + "type": "stringWithSelector" + }, + "parameters": [ + { + "description": "Object", + "name": "Object", + "supplementaryInformation": "ParticleEmitter3D::ParticleEmitter3D", + "type": "object" + } + ], + "objectGroups": [] + }, + { + "fullName": "", + "functionType": "ActionWithOperator", + "getterName": "Blending", + "group": "3D particle emitter color configuration", + "name": "SetBlending", + "private": true, + "sentence": "", + "events": [ + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [], + "actions": [ + { + "type": { + "value": "SetStringVariable" + }, + "parameters": [ + "Blending", + "=", + "Value" + ] + } + ] + }, + { + "type": "BuiltinCommonInstructions::JsCode", + "inlineCode": [ + "const object = objects[0];\r", + "const blending = eventsFunctionContext.getArgument(\"Value\");\r", + "\r", + "object.__particleEmitterAdapter.setBlending(blending);\r", + "" + ], + "parameterObjects": "Object", + "useStrict": true, + "eventsSheetExpanded": true + } + ], + "parameters": [ + { + "description": "Object", + "name": "Object", + "supplementaryInformation": "ParticleEmitter3D::ParticleEmitter3D", + "type": "object" + } + ], + "objectGroups": [] + }, + { + "description": "the gravity top of the object.", + "fullName": "Gravity top", + "functionType": "ExpressionAndCondition", + "group": "3D particle emitter speed configuration", + "name": "GravityTop", + "sentence": "the gravity top", + "events": [ + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [], + "actions": [ + { + "type": { + "value": "SetReturnString" + }, + "parameters": [ + "GravityTop" + ] + } + ] + } + ], + "expressionType": { + "supplementaryInformation": "[\"Y-\",\"Z+\"]", + "type": "stringWithSelector" + }, + "parameters": [ + { + "description": "Object", + "name": "Object", + "supplementaryInformation": "ParticleEmitter3D::ParticleEmitter3D", + "type": "object" + } + ], + "objectGroups": [] + }, + { + "fullName": "", + "functionType": "ActionWithOperator", + "getterName": "GravityTop", + "group": "3D particle emitter speed configuration", + "name": "SetGravityTop", + "sentence": "", + "events": [ + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [], + "actions": [ + { + "type": { + "value": "SetStringVariable" + }, + "parameters": [ + "GravityTop", + "=", + "Value" + ] + } + ] + }, + { + "type": "BuiltinCommonInstructions::JsCode", + "inlineCode": [ + "const object = objects[0];\r", + "const gravityTop = eventsFunctionContext.getArgument(\"Value\");\r", + "\r", + "object.__particleEmitterAdapter.setGravityTop(gravityTop);\r", + "" + ], + "parameterObjects": "Object", + "useStrict": true, + "eventsSheetExpanded": true + } + ], + "parameters": [ + { + "description": "Object", + "name": "Object", + "supplementaryInformation": "ParticleEmitter3D::ParticleEmitter3D", + "type": "object" + } + ], + "objectGroups": [] + }, + { + "description": "the gravity of the object.", + "fullName": "Gravity", + "functionType": "ExpressionAndCondition", + "group": "3D particle emitter speed configuration", + "name": "Gravity", + "sentence": "the gravity", + "events": [ + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [], + "actions": [ + { + "type": { + "value": "SetReturnNumber" + }, + "parameters": [ + "Gravity" + ] + } + ] + } + ], + "expressionType": { + "type": "expression" + }, + "parameters": [ + { + "description": "Object", + "name": "Object", + "supplementaryInformation": "ParticleEmitter3D::ParticleEmitter3D", + "type": "object" + } + ], + "objectGroups": [] + }, + { + "fullName": "", + "functionType": "ActionWithOperator", + "getterName": "Gravity", + "group": "3D particle emitter speed configuration", + "name": "SetGravity", + "sentence": "", + "events": [ + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [], + "actions": [ + { + "type": { + "value": "SetNumberVariable" + }, + "parameters": [ + "Gravity", + "=", + "Value" + ] + } + ] + }, + { + "type": "BuiltinCommonInstructions::JsCode", + "inlineCode": [ + "const object = objects[0];\r", + "const gravity = eventsFunctionContext.getArgument(\"Value\");\r", + "\r", + "object.__particleEmitterAdapter.setGravity(gravity);\r", + "" + ], + "parameterObjects": "Object", + "useStrict": true, + "eventsSheetExpanded": true + } + ], + "parameters": [ + { + "description": "Object", + "name": "Object", + "supplementaryInformation": "ParticleEmitter3D::ParticleEmitter3D", + "type": "object" + } + ], + "objectGroups": [] + }, + { + "description": "Check if delete when emission ends.", + "fullName": "Delete when emission ends", + "functionType": "Condition", + "group": "3D particle emitter configuration", + "name": "ShouldAutodestruct", + "sentence": "_PARAM0_ delete when emission ends", + "events": [ + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [ + { + "type": { + "value": "BooleanVariable" + }, + "parameters": [ + "ShouldAutodestruct", + "True", + "" + ] + } + ], + "actions": [ + { + "type": { + "value": "SetReturnBoolean" + }, + "parameters": [ + "True" + ] + } + ] + } + ], + "parameters": [ + { + "description": "Object", + "name": "Object", + "supplementaryInformation": "ParticleEmitter3D::ParticleEmitter3D", + "type": "object" + } + ], + "objectGroups": [] + }, + { + "description": "Change if delete when emission ends.", + "fullName": "Delete when emission ends", + "functionType": "Action", + "group": "3D particle emitter configuration", + "name": "SetShouldAutodestruct", + "sentence": "_PARAM0_ delete when emission ends: _PARAM1_", + "events": [ + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [ + { + "type": { + "inverted": true, + "value": "BooleanVariable" + }, + "parameters": [ + "Value", + "True", + "" + ] + } + ], + "actions": [ + { + "type": { + "value": "SetBooleanVariable" + }, + "parameters": [ + "ShouldAutodestruct", + "False", + "" + ] + } + ] + }, + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [ + { + "type": { + "value": "BooleanVariable" + }, + "parameters": [ + "Value", + "True", + "" + ] + } + ], + "actions": [ + { + "type": { + "value": "SetBooleanVariable" + }, + "parameters": [ + "ShouldAutodestruct", + "True", + "" + ] + } + ] + } + ], + "parameters": [ + { + "description": "Object", + "name": "Object", + "supplementaryInformation": "ParticleEmitter3D::ParticleEmitter3D", + "type": "object" + }, + { + "defaultValue": "yes", + "description": "ShouldAutodestruct", + "name": "Value", + "optional": true, + "type": "yesorno" + } + ], + "objectGroups": [] + }, + { + "description": "the start min trail length of the object.", + "fullName": "Start min trail length", + "functionType": "ExpressionAndCondition", + "group": "3D particle emitter trail configuration", + "name": "TrailStartLengthMin", + "sentence": "the start min trail length", + "events": [ + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [], + "actions": [ + { + "type": { + "value": "SetReturnNumber" + }, + "parameters": [ + "TrailStartLengthMin" + ] + } + ] + } + ], + "expressionType": { + "type": "expression" + }, + "parameters": [ + { + "description": "Object", + "name": "Object", + "supplementaryInformation": "ParticleEmitter3D::ParticleEmitter3D", + "type": "object" + } + ], + "objectGroups": [] + }, + { + "fullName": "", + "functionType": "ActionWithOperator", + "getterName": "TrailStartLengthMin", + "group": "3D particle emitter trail configuration", + "name": "SetTrailStartLengthMin", + "sentence": "", + "events": [ + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [], + "actions": [ + { + "type": { + "value": "SetNumberVariable" + }, + "parameters": [ + "TrailStartLengthMin", + "=", + "Value" + ] + } + ] + }, + { + "type": "BuiltinCommonInstructions::JsCode", + "inlineCode": [ + "const object = objects[0];\r", + "const value = eventsFunctionContext.getArgument(\"Value\");\r", + "\r", + "object.__particleEmitterAdapter.setTrailStartLengthMin(value);" + ], + "parameterObjects": "Object", + "useStrict": true, + "eventsSheetExpanded": false + } + ], + "parameters": [ + { + "description": "Object", + "name": "Object", + "supplementaryInformation": "ParticleEmitter3D::ParticleEmitter3D", + "type": "object" + } + ], + "objectGroups": [] + }, + { + "description": "the start max trail length of the object.", + "fullName": "Start max trail length", + "functionType": "ExpressionAndCondition", + "group": "3D particle emitter trail configuration", + "name": "TrailStartLengthMax", + "sentence": "the start max trail length", + "events": [ + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [], + "actions": [ + { + "type": { + "value": "SetReturnNumber" + }, + "parameters": [ + "TrailStartLengthMax" + ] + } + ] + } + ], + "expressionType": { + "type": "expression" + }, + "parameters": [ + { + "description": "Object", + "name": "Object", + "supplementaryInformation": "ParticleEmitter3D::ParticleEmitter3D", + "type": "object" + } + ], + "objectGroups": [] + }, + { + "fullName": "", + "functionType": "ActionWithOperator", + "getterName": "TrailStartLengthMax", + "group": "3D particle emitter trail configuration", + "name": "SetTrailStartLengthMax", + "sentence": "", + "events": [ + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [], + "actions": [ + { + "type": { + "value": "SetNumberVariable" + }, + "parameters": [ + "TrailStartLengthMax", + "=", + "Value" + ] + } + ] + }, + { + "type": "BuiltinCommonInstructions::JsCode", + "inlineCode": [ + "const object = objects[0];\r", + "const value = eventsFunctionContext.getArgument(\"Value\");\r", + "\r", + "object.__particleEmitterAdapter.setTrailStartLengthMax(value);" + ], + "parameterObjects": "Object", + "useStrict": true, + "eventsSheetExpanded": false + } + ], + "parameters": [ + { + "description": "Object", + "name": "Object", + "supplementaryInformation": "ParticleEmitter3D::ParticleEmitter3D", + "type": "object" + } + ], + "objectGroups": [] + }, + { + "description": "Check if the trail should follow the object.", + "fullName": "Trail following the object", + "functionType": "Condition", + "group": "3D particle emitter trail configuration", + "name": "IsTrailFollowingLocalOrigin", + "sentence": "The trail is following _PARAM0_ ", + "events": [ + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [ + { + "type": { + "value": "BooleanVariable" + }, + "parameters": [ + "IsTrailFollowingLocalOrigin", + "True", + "" + ] + } + ], + "actions": [ + { + "type": { + "value": "SetReturnBoolean" + }, + "parameters": [ + "True" + ] + } + ] + } + ], + "parameters": [ + { + "description": "Object", + "name": "Object", + "supplementaryInformation": "ParticleEmitter3D::ParticleEmitter3D", + "type": "object" + } + ], + "objectGroups": [] + }, + { + "description": "Change if the trail should follow the object or not.", + "fullName": "Make the trail follow", + "functionType": "Action", + "group": "3D particle emitter trail configuration", + "name": "SetIsTrailFollowingLocalOrigin", + "sentence": "Make the trail follow _PARAM0_: _PARAM1_", + "events": [ + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [ + { + "type": { + "value": "BooleanVariable" + }, + "parameters": [ + "Value", + "False", + "" + ] + } + ], + "actions": [ + { + "type": { + "value": "SetBooleanVariable" + }, + "parameters": [ + "IsTrailFollowingLocalOrigin", + "False", + "" + ] + } + ] + }, + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [ + { + "type": { + "value": "BooleanVariable" + }, + "parameters": [ + "Value", + "True", + "" + ] + } + ], + "actions": [ + { + "type": { + "value": "SetBooleanVariable" + }, + "parameters": [ + "IsTrailFollowingLocalOrigin", + "True", + "" + ] + } + ] + }, + { + "type": "BuiltinCommonInstructions::JsCode", + "inlineCode": [ + "const object = objects[0];\r", + "const value = eventsFunctionContext.getArgument(\"Value\");\r", + "\r", + "object.__particleEmitterAdapter.setTrailFollowingLocalOrigin(value);" + ], + "parameterObjects": "Object", + "useStrict": true, + "eventsSheetExpanded": false + } + ], + "parameters": [ + { + "description": "Object", + "name": "Object", + "supplementaryInformation": "ParticleEmitter3D::ParticleEmitter3D", + "type": "object" + }, + { + "defaultValue": "yes", + "description": "IsTrailFollowingLocalOrigin", + "name": "Value", + "optional": true, + "type": "yesorno" + } + ], + "objectGroups": [] + }, + { + "description": "the tail end width ratio of the object.", + "fullName": "Tail end width ratio", + "functionType": "ExpressionAndCondition", + "group": "3D particle emitter trail configuration", + "name": "TrailEndWidthRatio", + "sentence": "the tail end width ratio", + "events": [ + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [], + "actions": [ + { + "type": { + "value": "SetReturnNumber" + }, + "parameters": [ + "TrailEndWidthRatio" + ] + } + ] + } + ], + "expressionType": { + "type": "expression" + }, + "parameters": [ + { + "description": "Object", + "name": "Object", + "supplementaryInformation": "ParticleEmitter3D::ParticleEmitter3D", + "type": "object" + } + ], + "objectGroups": [] + }, + { + "fullName": "", + "functionType": "ActionWithOperator", + "getterName": "TrailEndWidthRatio", + "group": "3D particle emitter trail configuration", + "name": "SetTrailEndWidthRatio", + "sentence": "", + "events": [ + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [], + "actions": [ + { + "type": { + "value": "SetNumberVariable" + }, + "parameters": [ + "TrailEndWidthRatio", + "=", + "Value" + ] + } + ] + }, + { + "type": "BuiltinCommonInstructions::JsCode", + "inlineCode": [ + "const object = objects[0];\r", + "const value = eventsFunctionContext.getArgument(\"Value\");\r", + "\r", + "object.__particleEmitterAdapter.setTrailEndWidthRatio(value);" + ], + "parameterObjects": "Object", + "useStrict": true, + "eventsSheetExpanded": false + } + ], + "parameters": [ + { + "description": "Object", + "name": "Object", + "supplementaryInformation": "ParticleEmitter3D::ParticleEmitter3D", + "type": "object" + } + ], + "objectGroups": [] + }, + { + "description": "the render mode of the object.", + "fullName": "Render mode", + "functionType": "ExpressionAndCondition", + "group": "3D particle emitter configuration", + "name": "RenderMode", + "private": true, + "sentence": "the render mode", + "events": [ + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [], + "actions": [ + { + "type": { + "value": "SetReturnString" + }, + "parameters": [ + "RenderMode" + ] + } + ] + } + ], + "expressionType": { + "supplementaryInformation": "[\"Billboard\",\"Trail\"]", + "type": "stringWithSelector" + }, + "parameters": [ + { + "description": "Object", + "name": "Object", + "supplementaryInformation": "ParticleEmitter3D::ParticleEmitter3D", + "type": "object" + } + ], + "objectGroups": [] + }, + { + "fullName": "", + "functionType": "ActionWithOperator", + "getterName": "RenderMode", + "group": "3D particle emitter configuration", + "name": "SetRenderMode", + "private": true, + "sentence": "", + "events": [ + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [], + "actions": [ + { + "type": { + "value": "SetStringVariable" + }, + "parameters": [ + "RenderMode", + "=", + "Value" + ] + } + ] + }, + { + "type": "BuiltinCommonInstructions::JsCode", + "inlineCode": [ + "const object = objects[0];\r", + "const value = eventsFunctionContext.getArgument(\"Value\");\r", + "\r", + "object.__particleEmitterAdapter.setRenderMode(value);" + ], + "parameterObjects": "Object", + "useStrict": true, + "eventsSheetExpanded": false + }, + { + "type": "BuiltinCommonInstructions::Comment", + "color": { + "b": 109, + "g": 230, + "r": 255, + "textB": 0, + "textG": 0, + "textR": 0 + }, + "comment": "Update the trail properties because the RenderMode setter reset them" + }, + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [ + { + "type": { + "value": "StringVariable" + }, + "parameters": [ + "RenderMode", + "=", + "\"Trail\"" + ] + } + ], + "actions": [ + { + "type": { + "value": "ParticleEmitter3D::ParticleEmitter3D::SetTrailStartLengthMin" + }, + "parameters": [ + "Object", + "=", + "TrailStartLengthMin", + "" + ] + }, + { + "type": { + "value": "ParticleEmitter3D::ParticleEmitter3D::SetTrailStartLengthMax" + }, + "parameters": [ + "Object", + "=", + "TrailStartLengthMax", + "" + ] + } + ] + } + ], + "parameters": [ + { + "description": "Object", + "name": "Object", + "supplementaryInformation": "ParticleEmitter3D::ParticleEmitter3D", + "type": "object" + } + ], + "objectGroups": [] + } + ], + "eventsFunctionsFolderStructure": { + "folderName": "__ROOT", + "children": [ + { + "functionName": "onCreated" + }, + { + "functionName": "onDestroy" + }, + { + "functionName": "onHotReloading" + }, + { + "functionName": "UpdateFromProperties" + }, + { + "functionName": "UpdateHelper" + }, + { + "functionName": "UpdateImage" + }, + { + "functionName": "doStepPostEvents" + }, + { + "functionName": "RegisterInLayer" + }, + { + "functionName": "Delete" + }, + { + "functionName": "HasEnded" + }, + { + "functionName": "Restart" + }, + { + "folderName": "Position", + "children": [ + { + "functionName": "Z" + }, + { + "functionName": "SetZ" + } + ] + }, + { + "folderName": "Angle", + "children": [ + { + "functionName": "RotationX" + }, + { + "functionName": "SetRotationX" + }, + { + "functionName": "RotationY" + }, + { + "functionName": "SetRotationY" + } + ] + }, + { + "folderName": "3D particle emitter color configuration", + "children": [ + { + "functionName": "StartColor" + }, + { + "functionName": "SetStartColor" + }, + { + "functionName": "EndColor" + }, + { + "functionName": "SetEndColor" + }, + { + "functionName": "StartOpacity" + }, + { + "functionName": "SetStartOpacity" + }, + { + "functionName": "EndOpacity" + }, + { + "functionName": "SetEndOpacity" + }, + { + "functionName": "Blending" + }, + { + "functionName": "SetBlending" + } + ] + }, + { + "folderName": "3D particle emitter configuration", + "children": [ + { + "functionName": "Flow" + }, + { + "functionName": "SetFlow" + }, + { + "functionName": "StartSizeMin" + }, + { + "functionName": "SetStartSizeMin" + }, + { + "functionName": "StartSizeMax" + }, + { + "functionName": "SetStartSizeMax" + }, + { + "functionName": "EndScale" + }, + { + "functionName": "SetEndScale" + }, + { + "functionName": "LifespanMin" + }, + { + "functionName": "SetLifespanMin" + }, + { + "functionName": "LifespanMax" + }, + { + "functionName": "SetLifespanMax" + }, + { + "functionName": "Duration" + }, + { + "functionName": "SetDuration" + }, + { + "functionName": "SpayConeAngle" + }, + { + "functionName": "SetSpayConeAngle" + }, + { + "functionName": "ShouldAutodestruct" + }, + { + "functionName": "SetShouldAutodestruct" + }, + { + "functionName": "RenderMode" + }, + { + "functionName": "SetRenderMode" + } + ] + }, + { + "folderName": "3D particle emitter speed configuration", + "children": [ + { + "functionName": "StartSpeedMin" + }, + { + "functionName": "SetStartSpeedMin" + }, + { + "functionName": "StartSpeedMax" + }, + { + "functionName": "SetStartSpeedMax" + }, + { + "functionName": "AreParticlesRelative" + }, + { + "functionName": "SetAreParticlesRelative" + }, + { + "functionName": "GravityTop" + }, + { + "functionName": "SetGravityTop" + }, + { + "functionName": "Gravity" + }, + { + "functionName": "SetGravity" + } + ] + }, + { + "folderName": "3D particle emitter trail configuration", + "children": [ + { + "functionName": "TrailStartLengthMin" + }, + { + "functionName": "SetTrailStartLengthMin" + }, + { + "functionName": "TrailStartLengthMax" + }, + { + "functionName": "SetTrailStartLengthMax" + }, + { + "functionName": "IsTrailFollowingLocalOrigin" + }, + { + "functionName": "SetIsTrailFollowingLocalOrigin" + }, + { + "functionName": "TrailEndWidthRatio" + }, + { + "functionName": "SetTrailEndWidthRatio" + } + ] + } + ] + }, + "propertyDescriptors": [ + { + "value": "255;0;0", + "type": "Color", + "label": "Start color", + "group": "Color", + "name": "StartColor" + }, + { + "value": "255;255;0", + "type": "Color", + "label": "End color", + "group": "Color", + "name": "EndColor" + }, + { + "value": "255", + "type": "Number", + "unit": "Dimensionless", + "label": "Start opacity", + "group": "Color", + "name": "StartOpacity" + }, + { + "value": "0", + "type": "Number", + "unit": "Dimensionless", + "label": "End opacity", + "group": "Color", + "name": "EndOpacity" + }, + { + "value": "50", + "type": "Number", + "label": "Flow of particles (particles per second)", + "name": "Flow" + }, + { + "value": "10", + "type": "Number", + "unit": "Pixel", + "label": "Start min size", + "group": "Size", + "name": "StartSizeMin" + }, + { + "value": "20", + "type": "Number", + "unit": "Pixel", + "label": "Start max size", + "group": "Size", + "name": "StartSizeMax" + }, + { + "value": "0", + "type": "Number", + "unit": "Dimensionless", + "label": "End scale", + "group": "Size", + "name": "EndScale" + }, + { + "value": "100", + "type": "Number", + "unit": "PixelSpeed", + "label": "Start min speed", + "group": "Speed", + "name": "StartSpeedMin" + }, + { + "value": "100", + "type": "Number", + "unit": "PixelSpeed", + "label": "Start max speed", + "group": "Speed", + "name": "StartSpeedMax" + }, + { + "value": "1", + "type": "Number", + "unit": "Second", + "label": "Min lifespan", + "name": "LifespanMin" + }, + { + "value": "2", + "type": "Number", + "unit": "Second", + "label": "Max lifespan", + "name": "LifespanMax" + }, + { + "value": "5", + "type": "Number", + "unit": "Second", + "label": "Emission duration", + "name": "Duration" + }, + { + "value": "", + "type": "Boolean", + "label": "Particles move with the emitter", + "group": "Speed", + "name": "AreParticlesRelative" + }, + { + "value": "30", + "type": "Number", + "unit": "DegreeAngle", + "label": "Spay cone angle", + "group": "Position", + "name": "SpayConeAngle" + }, + { + "value": "Additive", + "type": "Choice", + "label": "Blending", + "group": "Color", + "choices": [ + { + "label": "Normal", + "value": "Normal" + }, + { + "label": "Additive", + "value": "Additive" + }, + { + "label": "Subtractive", + "value": "Subtractive" + }, + { + "label": "Multiply", + "value": "Multiply" + }, + { + "label": "None", + "value": "None" + } + ], + "name": "Blending" + }, + { + "value": "Y-", + "type": "Choice", + "label": "Gravity top", + "group": "Speed", + "choices": [ + { + "label": "Y-", + "value": "Y-" + }, + { + "label": "Z+", + "value": "Z+" + } + ], + "name": "GravityTop" + }, + { + "value": "0", + "type": "Number", + "unit": "PixelAcceleration", + "label": "Gravity", + "group": "Speed", + "name": "Gravity" + }, + { + "value": "true", + "type": "Boolean", + "label": "Delete when emission ends", + "name": "ShouldAutodestruct" + }, + { + "value": "Center-center", + "type": "String", + "label": "", + "description": "Only used by the scene editor.", + "hidden": true, + "name": "ParentOrigin" + }, + { + "value": "0", + "type": "Number", + "unit": "Pixel", + "label": "Z (elevation)", + "description": "Deprecated", + "group": "Position", + "hidden": true, + "name": "Z" + }, + { + "value": "0", + "type": "Number", + "unit": "DegreeAngle", + "label": "Rotation on X axis", + "group": "Position", + "hidden": true, + "name": "RotationX" + }, + { + "value": "0", + "type": "Number", + "unit": "DegreeAngle", + "label": "Rotation on Y axis", + "group": "Position", + "hidden": true, + "name": "RotationY" + }, + { + "value": "120", + "type": "Number", + "unit": "Pixel", + "label": "Start min length", + "group": "Trail", + "advanced": true, + "name": "TrailStartLengthMin" + }, + { + "value": "120", + "type": "Number", + "unit": "Pixel", + "label": "Start max length", + "group": "Trail", + "advanced": true, + "name": "TrailStartLengthMax" + }, + { + "value": "Billboard", + "type": "Choice", + "label": "Render mode", + "choices": [ + { + "label": "Image facing the camera", + "value": "Billboard" + }, + { + "label": "Image following a trail", + "value": "Trail" + } + ], + "advanced": true, + "name": "RenderMode" + }, + { + "value": "", + "type": "Boolean", + "label": "Follow the object", + "group": "Trail", + "advanced": true, + "name": "IsTrailFollowingLocalOrigin" + }, + { + "value": "0", + "type": "Number", + "unit": "Dimensionless", + "label": "Tail end width ratio", + "group": "Trail", + "advanced": true, + "name": "TrailEndWidthRatio" + } + ], + "propertiesFolderStructure": { + "folderName": "__ROOT", + "children": [ + { + "propertyName": "Flow" + }, + { + "propertyName": "LifespanMin" + }, + { + "propertyName": "LifespanMax" + }, + { + "propertyName": "Duration" + }, + { + "propertyName": "ShouldAutodestruct" + }, + { + "propertyName": "RenderMode" + }, + { + "folderName": "Color", + "children": [ + { + "propertyName": "StartColor" + }, + { + "propertyName": "EndColor" + }, + { + "propertyName": "StartOpacity" + }, + { + "propertyName": "EndOpacity" + }, + { + "propertyName": "Blending" + } + ] + }, + { + "folderName": "Size", + "children": [ + { + "propertyName": "StartSizeMin" + }, + { + "propertyName": "StartSizeMax" + }, + { + "propertyName": "EndScale" + } + ] + }, + { + "folderName": "Trail", + "children": [ + { + "propertyName": "TrailStartLengthMin" + }, + { + "propertyName": "TrailStartLengthMax" + }, + { + "propertyName": "TrailEndWidthRatio" + }, + { + "propertyName": "IsTrailFollowingLocalOrigin" + } + ] + }, + { + "folderName": "Speed", + "children": [ + { + "propertyName": "StartSpeedMin" + }, + { + "propertyName": "StartSpeedMax" + }, + { + "propertyName": "AreParticlesRelative" + }, + { + "propertyName": "GravityTop" + }, + { + "propertyName": "Gravity" + } + ] + }, + { + "folderName": "Position", + "children": [ + { + "propertyName": "SpayConeAngle" + }, + { + "propertyName": "Z" + }, + { + "propertyName": "RotationX" + }, + { + "propertyName": "RotationY" + } + ] + }, + { + "propertyName": "ParentOrigin" + } + ] + }, + "variants": [] + } + ] } ], "externalLayouts": [] diff --git a/examples/starting-3d-twin-stick-shooter/assets/Collision.wav b/examples/starting-3d-twin-stick-shooter/assets/Collision.wav new file mode 100644 index 0000000000000000000000000000000000000000..5b89dda9865db53229ee501b111b0612758f968b GIT binary patch literal 18870 zcmcKCc|26zA2{$qi;~htN!IL1Boawd2rZ~2>S^D%r}b%(C0a&GwiJ?5QK(28Eo2wj zx7nC6X2voYvoH5|&Ro;>^n8E6e}40Rz3!bE+`0G8x#ym9KA*$OZRN@=HwtZP&o>z{eFaq#%MCBv6+SBC?5W61#x|B@HGG zmT{n&12YHI2S)dd0S;3RQ;tw-aNmGTB6NWiNV-T;=p5BKmZ}IKcnm>~5YrIb5Z6#t z|E0dToKPEJuLB5VUu1veLD7O3z+D6#6dV*B5|H>Leg{8~m&bd}laoL z*cw3FU_0=z2RckTLOMz+A(RkG32Z8d%B60``{M)f<-`i2Uh|x0{pJ#KDZnN3fW7#= z_OHic`h;$}!{_@{D++ zxljk#vkv|XAygnKka!883Sot?Qn*VDeGovmhNA)QFb}xTy3a~wEfFjcI0)eOzH&D6 zHuJ`?#{##6&`}Y@mq4GypMk6VtNd&Hy8=jE1dSDr6)Fl%IdeJL{O5rB(5xYip>G_> zo(Cy%6glHKy?s#RAas2g>fl2DLVsa^&IK5i6TCF<$W5&nKkM|266g({0`+VQ?gU|Q9J@EF> z+eL-8g?5D@1)&9D1!@H|3e*c!-c5ct^_}q>(>G>s9KJ05(w$4sW#p#6efsv<+l04w z-X^})dZzPC_nGy5+xvF+xv7FwajN(c=CS_E8yEH}=1t7I zn2D5hxD%8VbT3Hry3X}E*AIss3p)|EXjDgg*** zid+%t8aWU>6wQv-ysUG1&SmRh+u$X^j={@=R|dNUdjzizz7mCvij10>q>-eVlzi>} zwTIW@t|VMZykdUU;;Plvpx}_;%fZ)zuLmoKsDw-nIgUMrJ%e?QSsCLNlX~OfjmI~p zhNy+8hkOYB6kHT+6SFvGNlZp;W^8t>SE^6y#?Ce)Ovr4lnvKFS>q}!%bK%u0kWu-k&%SqdIXZxL9cS2$= z#axbg5Rw-1B;-Y0Zd_j6lbDQ{%$N@$pF_Tcl!jD>REHEtmPJ-Z?vFkgeI&Z%LHUEK z2d`4!q`plpjjo9P8toIYF~Tn*^iuextCw8k-QzvuugBhqy%nn$svl|;Y8q-DY8kpP zbWx~XsKpJd8`d|pZtC2eb8~z6uJAqKudlqjl7D4K=} z_O+MSE{9$Vy&ifa{8o5u_|7Z4uk5>$8k!cG9x4ozgvo`!3Vjp$KD7GQw_EkM9wa|X zev*9j(eXzoA1NhIOrD&KO^HmonX)RyBgHeNG_5SHGR+}&S*&v`E)AbXN}K*b?ZM0k z%d?%cU9y#*PJF8JR4!{&*61wj_crerzfUN>Tbx*Yy5^snvo%)>v4s(Z{&@j;f92hM zmh>#;+5KlzpQ}Ef@w_tcYhHC;|4Zh}!I#My_cI=3?0&iL<$;%@Qpcu_OP!Ufk*byY z*OTo}c0SR2W$?=I)rr@qUY~ir{L_k0D?iPzwy3tOMw&3qa?P9TH`n{uUupMo-5;D@E`Pb= zWoCX>{`33=l~$G3l}#U8KDK^bQs7XqtiY*YV(FyP$)!)rpOt5ppKd(ec&4$7*hQog zUy<|3ugL`XqDz~kL((PPZA)xRYMVpR2hK1cb}zJ$Y)!TyYtwb;x^xB;3Sb4Wwy@lI zZoE}M74Iw1ENB6^5{QjJLsC=((!fCe5-1OWu1cVHLWm@Wm zrM9rP@HS!>sjH*QxWlBwv}0}uv60l+(HLHRx%x`=^_Iw%8!bMS8!9(e1{D2Ow5`bR z!{!eGAKddj^H=996pt<*TdY_-BY#%DM*j7p$f6rXcXIFMCgp|~UM{>=xUtB$$gilR zu&l7W@M;mZD55C+Q^u#vPg$R`b6@1X%pILEHe-B7cHZ;6oV<(=&pu>+V7+0#;l4@w zocuZE^VpnmIZ8RoIpUAVN6e>^oYI_%9A?&F)=<{1lQ$=C&)S)_J8N&& zjwidG?0u4&l$P{3X;Yei+Lp9M$%~VhBzIqDT<^Vp6B~_du1>74RUrWCRYcZW$b8Z>jin$Ye=k}eucV0hu_aOhlr0gl#)3O()+N3T{J(qAH zAt<3Pp)sK;;nm~UkKaAMnh=q2BOxlmD`tI+Z_L)1Z81AzW@0t5+E_iT0oE96ik*kG zz%Ilt!rEaSusfsoMDLGw!+K)ZVzm=>6ZI3(8j%~tj}k>8QEidc` z*fuNyYkSk~ro&A-winC94q-W1J~k;LB_cI~8cmPxjqZ-Z*AlOlo{;Lh9XAotJtq=e(>-YfNiSTkzQW@uJ7xIX*cXbN)#`mwq8VD7`AD zI;S@0er{^+!`wTWcQcbREfNQ18Equ!(6 zb4_+nUY$HPa$Mwu$l|+YcPs9Gy<3;wnBJW3@Y?b9^4Dt5)SqcQ+x*D?(bh-X9@S?w zWwd0ZWk1PI&rZom%}C4edgb$K{j2BMFSB#A^Rf+}nLL~O%=q!#$MYVWJT-f2{#5bp zxVK7gC%hFFiHgKUKA+cr-uRhTNv~v7Zm8Z^?OW|vom6wLCIwtCrZsbGNbRKdj`r5Z zw#N3xZ{@Y+_2mualF!Izxk8IV%R;NdwUz5Cy(<|7y#;**0|n7m7=4V@GFUYpeu6|qdw(hNO$)=LcCBs$hDsI*7uXn!Q{hIi-zM-L^v7xB_OM7wq zjQW}N>h`5Jeem?q8DL;&5I8jmRSZ`Q zR{|Bi5S>A1Fc{UO8qzm_P2o_u6xgaZ=-y26rvy;S2^EA&V1P788X_H~AEh6okEV{M zjsapiVmsnGVu^9U_14JN8?9f6#l#X~DN&9rPgWqu({BT!KCx8MaMAFWVIm(2=0eMb zsK??bfQA5?$AQc_CIcn|rUU2L=h)}j*m#&*Nv(gT{Wgku_~kaY4fvYB7sEcAPi6jfg#G({;U1h`W5@e0q~9p zA-za1rtcy>2oQljC3Ks4o0-5A(nWMJJ)|?V^HOJ6=TXpGJx1Du^8+^H=GR%&S=N22 zEvYT7J%~SqKMd@n?4#_bJZwt?8b}SKMp6@LPwU>+eXS{t_ZuHHW>#iZKCgUH`KIJ; z$@>z7>&_j!*Bo)n}PJS{M(G_9Ol*-_hBORnvz z-P^XWZGT&4T~=LoT}fGKS$SDS*~iAujfIWH((gRBEv_vd$iP3vKf_0M-vBz89n4N( z-k>>ftp9H*JQYtRP**dddQQD`w7^sPDtst>=b|iKrbsV4_q#Xj*QN5_% z)ClTzY9tlkkMAe+6Z_BiUg!<%wWZop7gO!10gNq-t&9tUfj|Tgy3U)=n$NOeedZTR zC5fPkf{B7j0woEmOaeU~VT(X%La0WJLdQ+K5Y#CQBrT+%pdP_Mfe7R$K`{kTw-9w- z3>_4r#L~wrKI9^Bl{z9k3Y_MljtGtjjtcB0s4hX5fGUs|$%E0F#1WJD(59hHLw)z4SoJMM@Cm0xl4M?Jc+3Rkg6SW8dsa@+7S$ts&J@8YqpF zSM7Q2Z`$9sYvQzVIyig{p@vvPs)?(Mue)70gD?}AMVL&RLYqok+PAdNvCp92px+Si zqx;b})A2L{K%~hN<%tT!MGdwMiyQ13HneO60-G*21vLdXDOQfJRH{7v<;<6}U(S8W z`k4Lk#mBQ%=c_JM1y%)?Un~zU$5vdgh^)9-QBzV^QeQ%<>!>5wQR*h)Rq&JX>zg(J zzD;FqJxC9xS0Eky5HZ{-9u!Z?Y@#Mn3n*(UYpQ6fY^wj( z@U7{aeEF#I(dCNe7a9T^E;g|1ICb1QUL73kDyiQ^*aPe(xKiDKX}!~XRe?HMJ*|P3 z+?(8cuQ#PPckmT3n?0MY3Dodvc;9%Y0@SbovK67s1@i$<3F?Fd3K?OIKVu+yw64V_LIU;B5YC1!vt3w_D{CHL&I%odE)8P_FavS`yh43?Uv$0bdI@Cq zWc6hCvRu09Mhi+p~tL0I8W1#6u)0L(HzLPgfQ73gxq5x*&&Bo6ik5`6p%J$>! zq}C}y<42=FwI($%W?&2xXdc}J_{*bmM!l5!G74IwxJGd`a7#H#s&?Erspv`3lWtAw zRDn8Fw5MxN*P5<5U2`^kxN5inD>a^LWCK|m@iXIQ#scM2%K(uQBveY6k}&1=l=vyr zRi^<{RbS420c7cC>Oa-b(4VL^QA=5Cg4QLY5TjtDAR{Hqah8gf#`BHl8_d_AKgW2E zv97U>v4(}Z#Y_t|i)l7fY$n^hb$H{D=kUrQ%rV3<$nm1%C{M(L@4@yM@ML&)c@jP8 zE4o&YR)o6;yPtDE?ao})v#M(qX;r$%LyvnN2_B0atsKoA6_!Zs1@;{KDyLGXBBu{d z5AE;S-?fjof8m(vnC|$(F4HdEF3oPW(<-MGPRpDW>=8SG9mh`5Nx=zO#$Tr5tmHh_ z*~-n_&BRULjqk#CVY)C}raDh@9`8KH*$=Z&zqLAI^_A6PH=f&&TfbYlOR&p1m(woOyeE1qdMkLB z`4swq0S2GaEuXi%-_q<`>s#Ub#W!I~^p@*e!nf#d(%3Y8)1*xb-eNDV*PxfX?+V|g zzP7&M{(=5y{57_yZkf1c>=wB#!hqp`z5r?fDWEN&KHzIWalprbHvtR0&Abi0b-XWn z{o{4qYlG)nPj}Cio+rExd+qbu;k72fHNYvrE}&t3_4=~)Me95K+x;8-zxo&Z7x=&S zf8mc^6Sn5!nlauOZ-Lja7knqf|AD`T-*mr8eu{pf{^$Hp`XBM%>%ZOK&)>&?wZDu1 zGJiY&VxNybZ+x`<)%~aVPw>~-G;`C`P0E|Lt=qhA{kk>lW;v@mPj;T*JkP8t8zWFJ<~lOY)sl1zcFf~-Da!JW}6K+>ujF2dFp26&0{uW zHVgb%e$@4(^=<1L*4O)0`W5?q^n2s?((jqyL%(Fdc)ut=tlt73Gao}A_q8k5I<8&3 zc7xwqzg2$A{g(JG^fUJ}_Dl6i^ojGi>7(c;?d}&?dIAU+J!E?xb*DOlS?;yZ17m?vC5;>wcfSH zwcK^L&tE=%K0ZDzo(-PWo@JgbBj053s&lJOuR69Wc4gE`?8?h4-+AYHXL&#IKIXgM zcZY9)Z@5>G*IBQVUL2nRAG#0OXYM+Kb=vFH*NyX4@D*(wy%t#`STnrF*Uih#!_CEQ z>IUTvqcryg;oi6v357@uy&z#@FyqjCfg|6jI$YSbISgh{XzS^ z_6aLvR^C{7ZKbo@Qn$r!R&F+q3moS;8ar-v+T^s}X^m6H>W8b7S0}8#?~>#a?-J$G z=u+cS;Zp2!)b4=Y9=jcOFKjYx(rwag(yi01?^`EYPc@%pu4F#W{H{^FQH;?oqh$t5 z4eSkU4H8UZO`=V1m}Kf?=seL$)3KO6f413d)7hG;8mhBYXR7W{+phMP+7`9+X^*Ep zn)Yy7fyO(HHyU{ww(2(O3)L5>f13VrdcpMf(;H^h&#IkOJj*=-qSQ!H&ZuNH&%C3U8%Z4)mb%2Gf?xq=0BPNYMa&k)HbR) z%&?zfH)GL^HtlBZM(uiS4fUDoGt^bpr>dLIG@fZV(_m)zEb1)std3bi4W0%^V_3se zdzH4U_6qG$bL8eAdLliXL5o47LA}8Y!)b<73?~^TY2DFE(2CQl(XZ04&@a({V))ST zzTrJXp)uE(Z9Hhq)auh>XwkH`83h<^Hri+uuN|X(OZ%qwa*bsgOEv5@7HiaKe$}kh zEYp0eny30oHAhunT~}R4T}yq3#x{+=Gy*i_W*}-}HIZ6^dc1m^dbGNkuBon(u7NH| z8?W82-J;!Q)MV6P^vx(wFGuf%UY6c7y?BFIgIflX2Hi$vBa#u`NKJ2=-ekRrdPW9w z40H{&436j=)Y-4IN2kZQ8w^vEj5{p}mTi_zmMIGo7u;SDv*6|e+WfBh9rN+?d*`Fi zLnK<%& zBx&YoJ=e<8da89<^O9zWW{~Dr^-A?}^-}e1)3#3AGR=RQyM`OM2d&UpuJKssq0W7s zWStMj?~L<|bB#0SJe~7oPTHJDhN*@rhKYv5dINg>dOdo4ZLT(3dr+IHeRRg*83$+V zpHZP!rdFy}tX4DY>#WLI<+HA7U)B!O4$($SW0X;((bKt4=BCZPKUZMNFF!eXw zWVqhY+fbq@(&TG$HA%Dav)gC4%x;?fPVco|uHH+%Q@Y1>kLe!P-DR}HXe;>Uu9#dh z2{s8d(KpsJ)-s-DJkxl>9K|_f<|xbw*ALYX(ht->W_H+Yzu6wM@OdHgF3vkQ@AN#P zRl8NQRh?Cnd7XK+d4>6D(-WpgO%Iwbnr}6K{`|S~!wfyN;{Q3Z3OTpLGg!-s`;4S*`D`@2bC2U(sl^k(?1?$eTYr ze?a;@tz6`^$YGJ~BEq89MU9JU7p-?#>)`IN(qWkc&t=G^&!yYtol~w;wo|&3lY_m3 zjl%+mDC_IiSFJBu2U%ZSe0K4v#m5#aSRxi83!Vkvl5II)*=tF&oMWqPJIhwpcAlMy zoxYv6-QQM+toB*$vRY-k!q(B&-qyzUf$crpJGODQur^g(IBMYst9Mp;RxhkFt(t6V zZNA!++YoG9Z5wQBY~>b;t$0?$Rs&YAO><1MO`n4L`#VytdjMOa<73bkrB zsW+)L`D)Swx=vksoq9o9fm&y^&S;&|GSM^8o1>?r_rdg?Dg66!3q~!#ED%}pEXSBB zm?3k8b4OVt3k3@~3(s1fvi#ffu;qTs7`sTjt9D^__brkv5-egZ^esRYWHG~Hn#E0% z2$QQO;U?O~vyIh_RgJHkUo#Ik4>1okzdA2$UhurYd2dW}O`n5(OgBxkO0bFrduVUE z$aJBprRjR}b>^PtuIA3>hs^ew?K0bDCN$t03>yp@?A6_+yIpsyuD|Yi&9jB>)(A1S9P*D2R3e^aheu2$Zw zxL0wH;%>#>@jc@il@yc<^o{FA|9*XXY`;_)6?N!>Nv|EV-lLW>Nk2M}=G|q6G0pK&%8}J(IFnWoU z{b;|jo5uQ%-8i;gu?=Vi){plAyvK_ag^B`2z9LUiceL(k9pH;%kzyh6Sy5%oq%jkL z1*0ti3t+)0ODWs2i^kdj)?;sux*?^YsGumXD5r=~e5#b8^hD{glK=S4z(Hw=5QwR);cgA~1=k!fRkRvYXh=?9<%S+%v#G+-_gmO~wedsapM5>S54=}j{ z+(W~MhmQ;&9X>YP3WIapb}k6?vt!t?>^SyR&Q#7c&UB6{M|DVTXvWaYAu6XEpmFFN z8k^2$uzSFetTJm7ONBL=l{uU>oIU(}_{Fdje>vY7Siv6+i~+{-UvOS>ayYphF$hf} zTnrDxd&GUreZox#7PIZx_Ut8W2ljbZAnPJ4h^5X^=gi`0aAtFA!I*HvVAEjppfSip zf%z_GoMv_lyA^<$E9%@?z$MNlP8cVgbD0w&?PG@xhYx45vRThrFIc--dsur}`&kEA zl3`>R!^W`X*e+aGt{Zn1*PUy{S;(>G*l-qcR&rc8t{gY%C!?R;Hrzgp1HFF2urhxl z7+q5VvUu6RbKVOc9}w__JXjK-kAjQBAgN%X0x(L7%%kwS04i^x5V8iM#nIvzp#8gi z2?R5UrXw=w2+wNCYRT*onvzUJ#tDNW$;nKTM@^KUGI}xqua_00oTZkJ>X(Q50Bt#$ zJUQqUgprYT#k&xh97N^sfWlz*#kp(CSIVImI5$bxvng%X)n5}Bjo zV^V*M*GSe#)=C|e$Q+WaLLhfw*LTkmnPnK6wHTR3H_lPxW-^)E+aBJ5|pk) zR|3lcS`xmAWt0osCtJ7tk=4X%X0@xR6C-VVJR${#8i`Z(0X>E-ls`Z-L_0LWMz&r#w` z;3#uq_%ZxgejGm@`1yE7v&OO%S>sqrtkVAS{>uKZ{Wbly{incw{lhxTI?rnAZ|QIA z$AMT7sed1+^$)TRv5v4_4ZR+E13vRT_{4Yp`TZaIKlK;(|JAdjXIIajp8Y)sdrtS9 z?K$5Q*b~$f(sKrkyIx>iWCSyI^zG`~)3>kh02qz6V%RWj8TJeZ#wS`4t%O!atE7FU zeWre)mQc&7Rn!{lLBqWfa|;`{D^ScQE5sQxkiiv8pI>zED9MrJd!mD$d0Vze^a z83YE2(a9LYP-G}Elo=|F<+PPFH<~+bHEkU&nQ|ZG;XI~fP_ii7={rHJXFvTQeFA+V zeKLI-U6nqQo(Cdt1t2E<1!TbCcI~4cq#mLEO+86HLoK8g(@JR-v?`h%V+q5Nv7E7j;mSy*r_mqN zGw7N0Y`R}pK-XVgJGypv?dy6;$|JoaeIR`%6_cien2{zyhoDb*Mtn}pA-*QQCw?TR zkRFnrke-sVNiRuJAm(_dExGMM+vB!##EZmG;$owGZ2I_Gp6b) zXIpn$PuoD-aGMj(1?P@i1M-c0anssow9jtWY1eNzY2QiOLplf|$R|jrNvb4uk|xL= z()-XuknZj%y8@NCz0G=Z2o9%OHIb=2VM zam~1P90^Ck9V8wlo*f;Vg01I6Isp&Kc*5^Te&g zZNS+O?TL;=XQC_7L;5{MgS>&e;C$b}#o%t^54RDkx_=os(X*tNlzr%mTf5DgGtMK3O4fqUF z7ARw|JbomdCj=3~2v-T$2~mVt!W4okK^>G$J%SNoE@3{wim-^_KyV_gBd;fKBKwp7 zBJU*cA|33-Hfgb#!w!ZGqm@;~JBwp_PCK83R-TgV0Z45x4{q0Ye-`oK4ZD%%K=jOey9RI;kIIVsJxk=#n}}P8+lhOK2Z%?&H`7RL1?eLlAa9UCyiG}@q)4y#r<81p7i9y*59D@i z1Ls!KEl(RwQ=}=;CW7c}Q1_+o@b0VK*Sl|aYl5h!KFt`!N9WNjX$yO8dKdRD>0R2p ztXGbyz#PpSE3Ks`GS~Kb^{wyo?epsk=sV6l#XQ42%RJ8vWCk&f2j&jU8<;;}Ibbz# zjP*C`1PiTq&#};U^dgQeXR)-6ZqAv{vEW#8tT;DD+F!6o5zj_gm|^+hQNyE$#|)=P z??-8@$EC%|c%$bOL!OFrJUL>0sY#r(hSbTd+s4SFlfD05bTE zc_z}nS*9RMkPSQst%DZ=il_^q0ItUyB4g+wDKep*0 zSSoM?mI<5$%LP;}4PbD4xV_vyE}UL~@G$~8fxJLLu!pyow~x1)M8 zzwvANb^LmM13yWa4BQ)O?FNVg#9O4cinofg-wUBF>lZWxbN4D9yp0z*Xi>;;Miao&(2`vy8Kx z<1FpHuH$*}ym{++8+aRezPwE!{@==L>Y_NYoU@3YUwPi=0I(fR!Q_;GrN*@CbM;cp^v__zMDnErP9rzXaO^ z+eg;h1UmqDrPT`SfO=uW$a%9AS_&5ktwwslFkj#k@EIrsoFve4DQAhZq*tm>A~RP4 znMuu)*dWlN@7j@HH6ty*Kr@|xETa)r41%&iED+0|$fC}&Udv2G{TJ3C#{%64bh$vX zKw>3@_NyjJCQ2qrsYoDIMC~UtkXe#h5)CP|U*8~Zlxh+~&C-mFDsh$gt5mhPT6|1` z`db1Wm-LJK#Z0LIG4%6K;m>zV_Kesofvm&}0c*epSOh2nE@5PDVSW~ciNb6gu>m7f zgnqA*>h^}RIBwYXaNu>m;8u3*Ei2(74@56$U2T~6uP?{9X_u)wAt-!e} zyb*kf1~U6QO=;c_Tn3E@Mt%m;_5J`P^8U*Q&pVzLsSZ{=WY8O#S>yc7^=fMrcQ_BVZcjFK!?x0`gx5k^l0iatGhb zmXeK={=EKonf&q2&-?%D^FM#r@ZCR^B-wlawbz3PeZ=qQ6TbdaX_LJt+w#BHIv5#k z%)cpM{+j{j&tMtU{BAbpcfZ%3zrG%e`Coo)_fO9>r1RzeYx$!N|I;%i%>VYMQvc(< zpIfl4$iIC;z!aE2Dny`X-{VBGcu^MeGcd{P7oU({l^}msipZd4`33U3mx#=d*5Cu= zR}T@CKY|KCP-q6{7G&#pj}hp}FVc};;gJ8kSBT7yet_)RYXtS94wprR;rh~dVMrJf zCl!zUa2xqS7OB~RKs!gsA||^LXwP?lBPdybOL06 z0(1dXm@F)L2fPQ+80H7b2kHL_fYFf8l0u*e_#!C=l#z+RBtQk23`~)licCeO0n-6h z0QQ*B)&Oh?z%Hu*5CUj>0=6VzBMF0G5IH~|0l{5>4RA)<5r6tSG5kaDcR7+=$txgF z@)~#}c?-Z}kVRo-M*w3vaR1RGL?g7apP@(hv@srw& zK=7LS`Fes^(_d2C5D2!{&^T_ObcRYGa#0FxQ=#n|*|mUP3FtKdTZspx4k8DULsEwk z2*%=$N*$B_KZu{pH7wOm(*Ft}E4i{#3;)xE8z74;dLSN%C$Jh=BefP;i>w1z3A{qk zs{>vc@Txe4oR)&~2GBf>v&h+zY;m+b`r`^(w5d(b^dwmY1esAM} zX@37xAM(3?*|GH$2%#rwc4bojkpfhV^%YXL}W$(z^cmH1h x+VlMVl>qr)eza@;{7jZ}^1s$%`qD$R`yp&Kp~ z^-1F#0b+&^?~!djl}Lah%Xi-SRd8Jo{?;3o-h)_@@SoEI=)F?aM7l^>yqk02c9S24QiaO4jwr%c}IEn=$@Iu|j z{A?dLBatx*q%rq|JS?4a|Lt#kE6DC0Rls21az!T6sTB=BB|cf0=hn7W5Tj2YYh_UTYzD|O1qCGKoBgnDQ^!7 zlxOgcU=(L3SI*wW5@3SY9PjRFnyg5Rw>yp@`_0$geX&9y2t^gk=3+ldi57KzAFG}Z zt#+FP$a-;l>dnP*u|mK!5AdGgZ(`akjHzkN;Zof2D7M=ex~}UY`H(K9HEdOE2BrL~ z=3J{pA`;v$; r5yj*z64LN`uAO!n*j9V(G7kR$?WdIAZVN{t00000NkvXXu0mjfzF)6> literal 0 HcmV?d00001 diff --git a/examples/starting-3d-twin-stick-shooter/assets/Flat dark joystick border.png b/examples/starting-3d-twin-stick-shooter/assets/Flat dark joystick border.png index 5ddd717e42b566f78f6441a2a92756a1e612dc74..b4e83e112eb9375cefbb86a6d044a972f8bf4dcf 100644 GIT binary patch literal 3751 zcmV;Y4p{MtP)005u}1^@s6i_d2*00006VoOIv0RI60 z0RN!9r;`8x010qNS#tmYE+YT{E+YYWr9XB6000McNliru>IW486)D;&{k8xA02y>e zSad^gZEa<4bO1wgWnpw>WFU8GbZ8()Nlj2!fese{01gjHL_t(|+U=cxXq@+Xz(3D> zC+SX79NUp>$yVSJ$>iiJ))lIUFb5nqkA@bSF^@SUfk8d!SOYF+9(=fJWpSxPJu{Ww|g-LpUR+CJNoe0RD#-Fx?Z zz*x5LUcdLeAD-v?eDC{yUoebKL@WgNh(uz+rWQAX1fn@1BG3aG0qFpq0NQ~LAn5&^ z&m55PexCtuflh)EfITSm(lNuxSW*0eB9GRYA65q#Of&1Dq9i`D~*IS=R#gJ@af2 zj`KC(dEj6T3HK;6z=yyua;ooNPMlAhfPRrKIz9et%)c z1gyd4AsE(!z+VD~e3a=*$&6<}hm+&y&zOL1TFX~8+}1|tj&1;^r>?WOFkkB#@jqOZzpk}x=+so!i|#vw z1TNINd`->Gbad{#`{yS*chc0{TV(oQ32JDOOiy-oAe7JDH zah!rByuFkmm&@Tpl8C;TyjZ>UhgB1>w|{Uz;55*KQOisyyp$Rr9jluX+#4Mn5;$k_ z`GOvSbG^~(%okQ%z^D5M4~lS_hwmaYYL|8qPCwm0c+l4+`04(^gASa+RZjp(-z>5~F8*BcRuCoU^i9H_Rz~wGxd<*~fr=(R~rG+co*> zX-o*G`}XdO)N}^ivp3eRsvpyMHwZNfNmTwInTVHdrFGddU?ds~A~t>^m~ zn{H_|GKhY8qw5JbRDvVXSO}4mCSOAmLgiE>8VhZjfZj6t0lO9rOVo3q`X#uxe{e|P zf9x6m?aypfs3UgP&wha;CZDmv zj`(QFC0O8F7DHo$yuRMIBhK38ci%y5!GW=H`n>1(6`27=Ubom8W#VX2O-OJ5vmJtS z#kyakOt`3YuGWmKo&m-AhRJ7?ksvDzSqdw`qI}b0W|Wz)7gU11ZFE1icp2qJbGau| zpIn$&KLd&!vQQb7Kpf|5g_Phwwon-r;)Qif@SZ)fb`9#ZH6TW%x?yo;+oenKxm7cu z26V{eGb-5)Yfy*QF2TZ(#mJ~;8GdjK_`Jo(s4&m1ItKK{e>d^Tv)hdN7zf_XfEvv0 zH~EZei~|YifmKSd9I)6J)j);S1Z2O3#;6hls|omw#l@%+U%IaeiNs>T;F2!d7|^Iz zBjPSfIGv8W4nR{T+h_6_)oet9ExE|u60G9bvptOJA`;CX14b+&Ms@uok}se=NZP0> z3cK?JR1?ssHc#aVD7NjIQEfW%OYnwwKiIN$8#}so<2a6mwoY*zht95DJicvvIS%&( z+?#rNqV&O}u)T99ZEY)$2X1Z&ky%`@t5t_Iw}c3XTM36-2?m?^eC7tKTI%82^93yF z;nGeZKsHwS`?p;rsd<{%XC`9+JCuwSK zF6HgPd;yEffZ^6Qc6Rp=2-=q?jZPpKq-$3X;kKfSXnVeZ1rus*rE_PuH5*1=AK=ir zql-|uwfJFN(eF?TxUrUqR9?W$;sP@_uA`c>h#LUpayh1_uCutXP_lq?#k^@@exAvz z6J%Z2qTk?TU6-kA6D%&w7yH)x?g=T{q~+z?T)%dOrA)@ILW8ojl;QfdD=aVHF6Hf+ zd;w=lO1;@EHzuz#Klg=QjRs?3evTX0ua-IocsB#ynMau~^(d;!XVX)-?lLVcA#N{O zMyywEFJ;IqF0zza;MVNTjUH|$U%;7-9_`l6&+Q5|K(ldU@@lybcPp<1>x_lhs4|mz z0!rHAVN{tbcLh|Kuy`0%=IXr+sA?I|s4^?gIRK85uz(m<)lW&@6>vH1q$y!C+AvDQ z&1Rf?0;c0}7c2`JRU?s3$KAXx7=YhdT#PF5$(W|=QDMf`d`<|$Z!JDXh51eXAMO`0#|IW4qr#kB zQ$WeeSsMo$6(l3BtSz8dn}20-GAhJ}`QyKbOYjO-hK)+`iwFN=6#;WUw>TMPYV_>nTXE< zzqGg+W#UX~JU&x+23$#*CoFD8nHXN{AJ!Dm4apf;SvGAL8z7w=A3amFfZi(UVT+%! zLEc&KAJ^ZWP2~hIYcVuRWY%kFOB2v*Ml1^&C86+R&!G}CARsDl<87s77)6jl<@lps z^hg0OCE}Bo1&tE2-*9Thos9|TWx?Z?1&wlNzk&tyvfy_uu0}btU%>);9r4@N5gP?C zDJpLj`r<&LFZ_IFGPAw2>lSdxVr;C#-y{>G9~S!Jq8`)Ww#W(W26kg@;_f5n{Wc_E zIv#gbdClT%tU5|l-b^RrnTiPrkV=e>06(;F8xJt-xj{t*^cs?HW4o#vdBxFHIabOW zOA;`dh|em#X>m6I6#k;v(pz*F{2(`HrhdP@v#T4}ZnB=M#!e%{1V+@3mASzTxT@D96d% zoS8bmy|b$m_=;VchTvaQg4 z1#_zM)l?#0_Jt@;CC)Z=@%)&=%h>cxJ&;l5~~2K|5#ML)^+@9v`f+2v$>q1ykkZ04u#>mPg-c-Jo- za>tG?THDs|qvq$n;Iru|-*mbPucgLE|8uiWKsGx9y?ggXMdc(gQ0rGqMA*^2o0c`k zf=ihUHzu#5s(z3!p(=;Vv82jZCHO&ZPEXBj>FEBEBXr_}<^78b^R%@+hU45nho5y_ zrY5hV{%AgiU6sS17Px&zU1z}ivf;{n;g5hwEnmH+re=2T>bWbSYL3aP6D%*^u66My zRCzO%7=6EH#@4kIotraL=`9`I!?m6bbGayN!>PpR`I;Km z)M~VPqk{)T_yG{B)pHEDwxSA|#f4fHZ%mcfO1Wvgp#=npL}Q_#=n>!_fG&(;m<9gM zU6d2)bi8ICIwXFGq_@)gE#P%*QNyFi0N+LB?P^ad*R6ne*b|L)spxl1#MQ-97)IrI zss_5-x)g8~5&s<6YNeK8Re9S_;`v&@{L-wVM+Ck_jZfWFfF#N=$O%7`+PW0*ei;W3 zBD?|oQ5~0O7x*R02{$8WYPq>+I05g_+aK#d^&2956F5*OA8Q2o87lAjIomaafDe@D z2%?98KLMWeQD$_3UjZMGlb37exR7 delta 1615 zcmV-V2C(_39pDU*B!3BTNLh0L01FZT01FZU(%pXi000IONkl$95AT zsS59mS44;|gnx(N+J#8cGO*RzEFP~zospvV09F@@5#H?{@q=XL22*Ds#!`x*5VwTo z4s|@d9%0$U>nUU)7Q7)|FM+)wU6p^Om0hR{xm+$Q0v6(ikQ?aY<#~Q6%d$d$CM1W~ zyWPhizDPv)k<3iubUOVV^8QI7_f&ihfrsPx+j*f+tbbe*UkoCwg&3a*Lr*ruVzFoy zUqm9@#3sJr>w85}TrEIx+~)>gFGPHR2>TfQ+$co2A>jZL6U2uIsq=v;-uI97Tf#!c zjkg1?`t4wW(l~eGoe1k|FBqpvXt>pz0R}xt%4c&^K9fwX4@^h5 zhA#J6GvxxN40aI`y1hS ze!&%-b_4%zQ{cc6n4Tf#%mZu^VFU=K0%QaW3=gfpBS1iakgx!G5nx&i9P-2F^Eody za({+iKu&O?c?w*XhG)!&Cg zfawTOXzfh}xLhv3JP-m*OMpTeFcIK);zNMx39zpSvp$9g3nr zfC#WcT9783hTwA|M1W}tu#fn9Cn4X75Pt!tBf!4ms|47$7kny01ek^ZhY;Uj!w8>6 zhyddk;1J>)d>Td%qz4@0!NA)Q`a##Y5+IinffdyyMU3Qw=}11;@`9lUeB)Rq*dEK| zW_&OlZvDyC9AGz&&+VvSn1Fn6JsVgGaLWaiV4DGRMK73Uz@Lqm0wxIvi3pe`Ab)&O z4Vd(R`Km7PX8*^R)qv^#k9r_D-2d(P!fNoN{&CV;a6gbQJqatw?@HG=xAlWdCg|wAjV`RfH>hw#SQIrNJ#pg@LH)z6u$;Y3pt1%-8PLq%Cl9A&N?sF9;)t-u>03mkWhy=6?j?_NzDg8eR}?-TU?Se(VLoY37R;1oO<7?g%*V zdUWWH@ZG{NT+g?wF|Z1wmtB|_gj>-zEpuT^O@q_WODG2O&`T%==UFe4)8M4t)~7(t z;3}uwOl>lm+|wnY*K2C(`>`l#B^`1+nNP;R0`)#0M7U*vm~t^S`o$ERO@FN(-J(;1 z_z)o^D87>#A>lauQ*FPqkd4HIgaZ*my5d8Ghz}70;``jT3$b^+TCEOcSzd+yZiu~I zv#}}$7~0A6{AvLfMG>0U`|xJ4fLh_%q035G*N9Jlml|If9m<>K|aWyu_P!t1d&3|x5Z z^#%&djQe*yv*GR7eBdP;T6rzP(=RLGcKmF4OKAR@U`TkaUxkVd>k&09nX(WY;h_fm z(7aCR2!JNd+2eUyzL8gq@wyPgY+$C*BJCAJ7NfknL`Nwlg2YAF$w>KrNUF|acY0Pb z+4w{CTkRp=E%$PDBDE&8BFg(ZY;T0bA^K$5`q@vPQ+=-f4MOTa`w!ht=DUf6Hmd*t N002ovPDHLkV1i6p4Cnv= diff --git a/examples/starting-3d-twin-stick-shooter/assets/Flat dark joystick thumb.png b/examples/starting-3d-twin-stick-shooter/assets/Flat dark joystick thumb.png index 36f5262a9055b15bf36ba5ddee957e1fa854561e..8acf179f8660be36ccea1198105da35c07154fd7 100644 GIT binary patch delta 2913 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 LNkvXXu0mjflp>}T delta 1022 zcmV zyp+Nh;58IdxZlj$Jks-6fY(q+p8T89>-ApBhkP54$Db&`{}j15xktIz)J8ls@b?s9 z9{9mv@Ykm^RWMB-@(kvH@AvzEBx@c2^>wO{Y^$C3K^KuW9oai^UE2XIUX&sFaK*sK=W{nW8pTH)DjRkD8u6f%9&P z_H?`!95P40;#H&q_SjPoLek}gJY{|;sVM+6CpkADuH4L&VMeSxd7EYkV44!m zkn%)5PST5_fSl7*(dLKI8AmKX*IAg4U?|_FW7UTT$X?><>&pI$T~ttxsnfmA7bqlI;u=xnoS&PUnpeW$T$GwvlA5AW zo>`Ki;O^-g5Z=fq4pem6)5S5Q;?~<+zFdb4M2>tsnLVK{U{}GzzwgyN9FGRARhsqu zny^aupXW@%b+fmhXn9=o^~Mao-=3DmR`;h&@}A`NZFb~^8{1FK&3qvu9sKg&gLmt? zl2_RPO~wbVOtOd<>vdkf?{2E@y|{ec@4jMAH~*bC&y3vu=F{uPE2qRP{p~yHjMF#v zCHwtfPWtc}*5j~)%+YxJC0e?CWdeaG0_N&;baChf9aW0Z$ZZXi_0jv=q{i#n)xG#ZRKbVj zjjtX&TC=)&wE{<7!wzo0F1?^f|0g+hOYryo{`l_R<9D(Tazkb${W#9(YWg+AMlGdy zwbKJpTM0Lgx-(s8E(pCm$`f<^x~)yk-tJF2-;3+}Cd@IHcp%vEe8#fbN2HHjp1tf? z<$Q}XUWDC7hOjdifNcU$(6*udHzdidj*}vRp+MDf{zqWRc z9fRbnte+k)2`u*>=2^U%UOEGhMUrs?@jV_u`e6woS>i zzxegW_ZvZ6K;^snGw#=bw_8yAoe{{m zpA0nV_d8~zkKr5C9U3?USUMOSiJ^L=7yPdHwwvvR-8`U4Z+G(p8Gq$~CfzERpU}C# ziPWF~xy97s`^PsB&jA_VZR|A6^>bP0l+XkK=p&wV diff --git a/examples/starting-3d-twin-stick-shooter/assets/starting_bulletfire.wav b/examples/starting-3d-twin-stick-shooter/assets/starting_bulletfire.wav new file mode 100644 index 0000000000000000000000000000000000000000..f7045dd18c0e6f5125fccbccfc36f5023656a915 GIT binary patch literal 6148 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 edhy=ZUaI$;!{ literal 0 HcmV?d00001 diff --git a/examples/starting-3d-twin-stick-shooter/starting-3d-twin-stick-shooter.json b/examples/starting-3d-twin-stick-shooter/starting-3d-twin-stick-shooter.json index f5d5e4a9c..ffcdd8d96 100644 --- a/examples/starting-3d-twin-stick-shooter/starting-3d-twin-stick-shooter.json +++ b/examples/starting-3d-twin-stick-shooter/starting-3d-twin-stick-shooter.json @@ -1,7 +1,7 @@ { "firstLayout": "", "gdVersion": { - "build": 272, + "build": 274, "major": 5, "minor": 6, "revision": 0 @@ -169,6 +169,34 @@ "metadata": "", "name": "assets\\aircraft_red.glb", "userAdded": false + }, + { + "file": "assets/DirtParticle.png", + "kind": "image", + "metadata": "", + "name": "assets/DirtParticle.png", + "smoothed": true, + "userAdded": false + }, + { + "file": "assets/Collision.wav", + "kind": "audio", + "metadata": "", + "name": "assets/Collision.wav", + "preloadAsMusic": false, + "preloadAsSound": false, + "preloadInCache": false, + "userAdded": false + }, + { + "file": "assets/starting_bulletfire.wav", + "kind": "audio", + "metadata": "", + "name": "assets/starting_bulletfire.wav", + "preloadAsMusic": false, + "preloadAsSound": false, + "preloadInCache": false, + "userAdded": false } ] }, @@ -595,6 +623,30 @@ "BulletLayer": "", "RandomizedAngle": 0 }, + { + "name": "ShakeModel3D", + "type": "ShakeObject3D::ShakeModel3D", + "TranslationAmplitudeX": 0, + "TranslationAmplitudeY": 0, + "TranslationAmplitudeZ": 0, + "RotationAmplitudeX": 15, + "RotationAmplitudeY": 5, + "RotationAmplitudeZ": 5, + "Time": 0, + "Duration": 0, + "StartEasingDuration": 0, + "StopEasingDuration": 0, + "Frequency": 20, + "DeltaX": 0, + "DeltaY": 0, + "DeltaZ": 0, + "DeltaAngleX": 0, + "DeltaAngleY": 0, + "DeltaAngleZ": 0, + "NoiseTime": 0, + "Object3D": "Object3D", + "IsStartingAtCreation": false + }, { "isFolded": true, "name": "SmoothCamera", @@ -695,16 +747,43 @@ { "assetStoreId": "", "name": "Enemy", + "persistentUuid": "436ab02a-f46e-4ef5-a893-aed5b7ecf36c", "type": "Scene3D::Model3DObject", "variables": [ { "name": "Health", + "persistentUuid": "0585b174-382a-4c72-b8a8-65da8e3c5110", "type": "number", "value": 3 } ], "effects": [], - "behaviors": [], + "behaviors": [ + { + "name": "ShakeModel3D", + "type": "ShakeObject3D::ShakeModel3D", + "TranslationAmplitudeX": 5, + "TranslationAmplitudeY": 5, + "TranslationAmplitudeZ": 0, + "RotationAmplitudeX": 5, + "RotationAmplitudeY": 10, + "RotationAmplitudeZ": 5, + "Time": 0, + "Duration": 0, + "StartEasingDuration": 0, + "StopEasingDuration": 0, + "Frequency": 7, + "DeltaX": 0, + "DeltaY": 0, + "DeltaZ": 0, + "DeltaAngleX": 0, + "DeltaAngleY": 0, + "DeltaAngleZ": 0, + "NoiseTime": 0, + "Object3D": "Object3D", + "IsStartingAtCreation": false + } + ], "content": { "centerLocation": "ModelOrigin", "crossfadeDuration": 0.10000000149011612, @@ -753,6 +832,168 @@ "width": 33.45138142032624, "animations": [] } + }, + { + "assetStoreId": "", + "name": "DebrisParticle", + "persistentUuid": "00a72558-e390-4ccf-871c-2f04ff9bcde5", + "type": "ParticleEmitter3D::ParticleEmitter3D", + "variant": "", + "variables": [], + "effects": [], + "behaviors": [], + "content": { + "EndColor": "255;255;255", + "StartColor": "109;109;109", + "SpayConeAngle": 360, + "EndScale": 0.5, + "Gravity": 600, + "StartSpeedMax": 250, + "StartSizeMin": 16, + "Duration": 0.01, + "Flow": 2000, + "LifespanMin": 0.075, + "LifespanMax": 0.25, + "StartSizeMax": 16, + "StartSpeedMin": 150 + }, + "childrenContent": { + "Particle": { + "adaptCollisionMaskAutomatically": true, + "updateIfNotVisible": false, + "animations": [ + { + "name": "Image", + "useMultipleDirections": false, + "directions": [ + { + "looping": false, + "timeBetweenFrames": 0.08, + "sprites": [ + { + "hasCustomCollisionMask": true, + "image": "assets/DirtParticle.png", + "points": [], + "originPoint": { + "name": "origine", + "x": 0, + "y": 0 + }, + "centerPoint": { + "automatic": true, + "name": "centre", + "x": 0, + "y": 0 + }, + "customCollisionMask": [ + [ + { + "x": 0, + "y": 0 + }, + { + "x": 32, + "y": 0 + }, + { + "x": 32, + "y": 32 + }, + { + "x": 0, + "y": 32 + } + ] + ] + } + ] + } + ] + } + ] + } + } + }, + { + "assetStoreId": "", + "name": "LargeDebrisParticle", + "persistentUuid": "e66ebb77-4d1b-4b6f-9351-1a9fa9afd063", + "type": "ParticleEmitter3D::ParticleEmitter3D", + "variant": "", + "variables": [], + "effects": [], + "behaviors": [], + "content": { + "EndColor": "255;255;255", + "StartColor": "109;109;109", + "SpayConeAngle": 360, + "EndScale": 1, + "Gravity": 600, + "StartSpeedMax": 500, + "StartSizeMin": 32, + "Duration": 0.01, + "Flow": 2000, + "LifespanMin": 0.075, + "LifespanMax": 0.25, + "StartSizeMax": 32, + "StartSpeedMin": 300 + }, + "childrenContent": { + "Particle": { + "adaptCollisionMaskAutomatically": true, + "updateIfNotVisible": false, + "animations": [ + { + "name": "Image", + "useMultipleDirections": false, + "directions": [ + { + "looping": false, + "timeBetweenFrames": 0.08, + "sprites": [ + { + "hasCustomCollisionMask": true, + "image": "assets/DirtParticle.png", + "points": [], + "originPoint": { + "name": "origine", + "x": 0, + "y": 0 + }, + "centerPoint": { + "automatic": true, + "name": "centre", + "x": 0, + "y": 0 + }, + "customCollisionMask": [ + [ + { + "x": 0, + "y": 0 + }, + { + "x": 32, + "y": 0 + }, + { + "x": 32, + "y": 32 + }, + { + "x": 0, + "y": 32 + } + ] + ] + } + ] + } + ] + } + ] + } + } } ], "objectsFolderStructure": { @@ -767,6 +1008,12 @@ { "objectName": "Enemy" }, + { + "objectName": "DebrisParticle" + }, + { + "objectName": "LargeDebrisParticle" + }, { "objectName": "RepeatingBackground" }, @@ -1044,6 +1291,35 @@ } ] }, + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [ + { + "type": { + "value": "FireBullet::FireBullet::HasJustFired" + }, + "parameters": [ + "Player", + "FireBullet", + "" + ] + } + ], + "actions": [ + { + "type": { + "value": "PlaySound" + }, + "parameters": [ + "", + "assets/starting_bulletfire.wav", + "", + "20", + "RandomWithStep(0.9,1.2,0.05)" + ] + } + ] + }, { "type": "BuiltinCommonInstructions::Comment", "color": { @@ -1084,6 +1360,43 @@ "1" ] }, + { + "type": { + "value": "Create" + }, + "parameters": [ + "", + "DebrisParticle", + "PlayerBullet.BoundingBoxRight()", + "PlayerBullet.CenterY()", + "" + ] + }, + { + "type": { + "value": "ShakeObject3D::ShakeModel3D::Shake" + }, + "parameters": [ + "Enemy", + "ShakeModel3D", + "0.25", + "0", + "0.2", + "" + ] + }, + { + "type": { + "value": "PlaySound" + }, + "parameters": [ + "", + "assets/Collision.wav", + "", + "30", + "RandomWithStep(0.9,1.1,0.05)" + ] + }, { "type": { "value": "Delete" @@ -1111,6 +1424,18 @@ } ], "actions": [ + { + "type": { + "value": "Create" + }, + "parameters": [ + "", + "LargeDebrisParticle", + "Enemy.CenterX()", + "Enemy.CenterY()", + "" + ] + }, { "type": { "value": "Delete" @@ -1136,9 +1461,69 @@ "", "" ] + }, + { + "type": { + "value": "BuiltinCommonInstructions::Once" + }, + "parameters": [] } ], "actions": [ + { + "type": { + "value": "ShakeObject3D::ShakeModel3D::Shake" + }, + "parameters": [ + "Player", + "ShakeModel3D", + "2", + "0", + "0", + "" + ] + }, + { + "type": { + "value": "PlaySound" + }, + "parameters": [ + "", + "assets/Collision.wav", + "", + "90", + "RandomFloatInRange(0.7,0.9)" + ] + }, + { + "type": { + "value": "Create" + }, + "parameters": [ + "", + "LargeDebrisParticle", + "Player.CenterX()", + "Player.CenterY()", + "" + ] + }, + { + "type": { + "value": "ChangeTimeScale" + }, + "parameters": [ + "", + "0.15" + ] + }, + { + "type": { + "value": "Wait" + }, + "parameters": [ + "0.15" + ] + }, { "type": { "value": "Scene" @@ -1270,6 +1655,11 @@ "name": "Scale", "type": "ScalableCapability::ScalableBehavior" }, + { + "name": "ShakeModel3D", + "type": "ShakeObject3D::ShakeModel3D", + "EasingFactor": 0 + }, { "name": "SmoothCamera", "type": "SmoothCamera::SmoothCamera" @@ -21145,6 +21535,14107 @@ }, "eventsBasedBehaviors": [], "eventsBasedObjects": [] + }, + { + "author": "", + "category": "Visual effect", + "dimension": "3D", + "extensionNamespace": "", + "fullName": "3D object shake", + "gdevelopVersion": ">=5.5.222", + "helpPath": "", + "iconUrl": "data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iVVRGLTgiPz48IURPQ1RZUEUgc3ZnIFBVQkxJQyAiLS8vVzNDLy9EVEQgU1ZHIDEuMS8vRU4iICJodHRwOi8vd3d3LnczLm9yZy9HcmFwaGljcy9TVkcvMS4xL0RURC9zdmcxMS5kdGQiPjxzdmcgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIiB4bWxuczp4bGluaz0iaHR0cDovL3d3dy53My5vcmcvMTk5OS94bGluayIgdmVyc2lvbj0iMS4xIiBpZD0ibWRpLXZlY3Rvci1kaWZmZXJlbmNlLWFiIiB3aWR0aD0iMjQiIGhlaWdodD0iMjQiIHZpZXdCb3g9IjAgMCAyNCAyNCI+PHBhdGggZD0iTTMsMUMxLjg5LDEgMSwxLjg5IDEsM1Y1SDNWM0g1VjFIM003LDFWM0gxMFYxSDdNMTIsMVYzSDE0VjVIMTZWM0MxNiwxLjg5IDE1LjExLDEgMTQsMUgxMk0xLDdWMTBIM1Y3SDFNMTQsN0MxNCw3IDE0LDExLjY3IDE0LDE0QzExLjY3LDE0IDcsMTQgNywxNEM3LDE0IDcsMTggNywyMEM3LDIxLjExIDcuODksMjIgOSwyMkgyMEMyMS4xMSwyMiAyMiwyMS4xMSAyMiwyMFY5QzIyLDcuODkgMjEuMTEsNyAyMCw3QzE4LDcgMTQsNyAxNCw3TTE2LDlIMjBWMjBIOVYxNkgxNEMxNS4xMSwxNiAxNiwxNS4xMSAxNiwxNFY5TTEsMTJWMTRDMSwxNS4xMSAxLjg5LDE2IDMsMTZINVYxNEgzVjEySDFaIiAvPjwvc3ZnPg==", + "name": "ShakeObject3D", + "previewIconUrl": "https://resources.gdevelop-app.com/assets/Icons/vector-difference-ab.svg", + "shortDescription": "Shake 3D objects.", + "version": "2.1.1", + "description": [ + "Shake 3D objects with translation and rotation.", + "", + "The 3D racing game example uses this extension ([open the project online](https://editor.gdevelop.io/?project=example://3d-racing-game))." + ], + "origin": { + "identifier": "ShakeObject3D", + "name": "gdevelop-extension-store" + }, + "tags": [ + "3d", + "shaking", + "effect", + "shake", + "translate", + "rotate" + ], + "authorIds": [ + "IWykYNRvhCZBN3vEgKEbBPOR3Oc2" + ], + "changelog": [ + { + "version": "2.0.0", + "breaking": "- The behavior for 3D box has been removed. The other behavior can be used for both models and boxes." + } + ], + "dependencies": [], + "globalVariables": [], + "sceneVariables": [], + "eventsFunctions": [ + { + "fullName": "", + "functionType": "Action", + "name": "onFirstSceneLoaded", + "sentence": "", + "events": [ + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [], + "actions": [ + { + "type": { + "value": "ShakeObject3D::DefineHelperClasses" + }, + "parameters": [ + "", + "" + ] + } + ] + } + ], + "parameters": [], + "objectGroups": [] + }, + { + "description": "Define helper classes JavaScript code.", + "fullName": "Define helper classes", + "functionType": "Action", + "name": "DefineHelperClasses", + "private": true, + "sentence": "Define helper classes JavaScript code", + "events": [ + { + "type": "BuiltinCommonInstructions::JsCode", + "inlineCode": [ + "if (gdjs._shakeObjectExtension) {", + " return;", + "}", + "", + "/** Noise generator manager. */", + "class NoiseManager {", + " /**", + " * Create the manager of noise generators.", + " */", + " constructor() {", + " this.seed = gdjs.randomInRange(1, Number.MAX_SAFE_INTEGER);", + " /** @type {Map} */", + " this.generators = new Map();", + " }", + "", + " /**", + " * @param name {string}", + " * @return {NoiseGenerator}", + " */", + " getGenerator(name) {", + " let generator = this.generators.get(name);", + " if (!generator) {", + " generator = new NoiseGenerator(name + this.seed);", + " this.generators.set(name, generator);", + " }", + " return generator;", + " }", + "", + " /**", + " * @param seed {number}", + " */", + " setSeed(seed) {", + " this.seed = seed;", + " this.generators.forEach(generator => generator.setSeed(name + this.seed));", + " }", + "", + " /**", + " * @param name {string}", + " */", + " deleteGenerator(name) {", + " this.generators.delete(name);", + " }", + "", + " /**", + " */", + " deleteAllGenerators() {", + " this.generators.clear();", + " }", + "}", + "", + "/** Noise generator with octaves. */", + "class NoiseGenerator {", + " /**", + " * Create a noise generator with a seed.", + " * @param seed {string}", + " */", + " constructor(seed) {", + " this.simplexNoise = new SimplexNoise(seed);", + " this.frequency = 1;", + " this.octaves = 1;", + " this.persistence = 0.5;", + " this.lacunarity = 2;", + " this.xLoopPeriod = 0;", + " this.yLoopPeriod = 0;", + " }", + "", + " /**", + " * @param seed {string}", + " */", + " setSeed(seed) {", + " this.simplexNoise = new SimplexNoise(seed);", + " }", + "", + " /**", + " * @param x {float}", + " * @param y {float}", + " * @param z {float} optionnal", + " * @param w {float} optionnal", + " * @return {float}", + " */", + " noise(x, y, z, w) {", + " if (this.xLoopPeriod && this.yLoopPeriod) {", + " const circleRatioX = 2 * Math.PI / this.xLoopPeriod;", + " const circleRatioY = 2 * Math.PI / this.yLoopPeriod;", + " const angleX = circleRatioX * x;", + " const angleY = circleRatioY * y;", + " x = Math.cos(angleX) / circleRatioX;", + " y = Math.sin(angleX) / circleRatioX;", + " z = Math.cos(angleY) / circleRatioY;", + " w = Math.sin(angleY) / circleRatioY;", + " }", + " else if (this.xLoopPeriod) {", + " const circleRatio = 2 * Math.PI / this.xLoopPeriod;", + " const angleX = circleRatio * x;", + " w = z;", + " z = y;", + " x = Math.cos(angleX) / circleRatio;", + " y = Math.sin(angleX) / circleRatio;", + " }", + " else if (this.yLoopPeriod) {", + " const circleRatio = 2 * Math.PI / this.xLoopPeriod;", + " const angleX = circleRatio * x;", + " w = z;", + " // Make the circle perimeter equals to the looping period", + " // to keep the same perceived frequency with or without looping.", + " y = Math.cos(angleX) / circleRatio;", + " z = Math.sin(angleX) / circleRatio;", + " }", + " let noiseFunction = this.simplexNoise.noise4D.bind(this.simplexNoise);", + " if (z === undefined) {", + " noiseFunction = this.simplexNoise.noise2D.bind(this.simplexNoise);", + " }", + " else if (w === undefined) {", + " noiseFunction = this.simplexNoise.noise3D.bind(this.simplexNoise);", + " }", + " let frequency = this.frequency;", + " let noiseSum = 0;", + " let amplitudeSum = 0;", + " let amplitude = 1;", + " for (let i = 0; i < this.octaves; i++) {", + " noiseSum += noiseFunction(x * frequency, y * frequency, z * frequency, w * frequency) * amplitude;", + " amplitudeSum += Math.abs(amplitude);", + " amplitude *= this.persistence;", + " frequency *= this.lacunarity;", + " }", + " return noiseSum / amplitudeSum;", + " }", + "}", + "", + "/*", + "A fast javascript implementation of simplex noise by Jonas Wagner", + "https://github.com/jwagner/simplex-noise.js", + "", + "Based on a speed-improved simplex noise algorithm for 2D, 3D and 4D in Java.", + "Which is based on example code by Stefan Gustavson (stegu@itn.liu.se).", + "With Optimisations by Peter Eastman (peastman@drizzle.stanford.edu).", + "Better rank ordering method by Stefan Gustavson in 2012.", + "", + " Copyright (c) 2021 Jonas Wagner", + "", + " Permission is hereby granted, free of charge, to any person obtaining a copy", + " of this software and associated documentation files (the \"Software\"), to deal", + " in the Software without restriction, including without limitation the rights", + " to use, copy, modify, merge, publish, distribute, sublicense, and/or sell", + " copies of the Software, and to permit persons to whom the Software is", + " furnished to do so, subject to the following conditions:", + "", + " The above copyright notice and this permission notice shall be included in all", + " copies or substantial portions of the Software.", + "", + " THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR", + " IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,", + " FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE", + " AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER", + " LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,", + " OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE", + " SOFTWARE.", + " */", + "", + "const F2 = 0.5 * (Math.sqrt(3.0) - 1.0);", + "const G2 = (3.0 - Math.sqrt(3.0)) / 6.0;", + "const F3 = 1.0 / 3.0;", + "const G3 = 1.0 / 6.0;", + "const F4 = (Math.sqrt(5.0) - 1.0) / 4.0;", + "const G4 = (5.0 - Math.sqrt(5.0)) / 20.0;", + "const grad3 = new Float32Array([1, 1, 0,", + " -1, 1, 0,", + " 1, -1, 0,", + " -1, -1, 0,", + " 1, 0, 1,", + " -1, 0, 1,", + " 1, 0, -1,", + " -1, 0, -1,", + " 0, 1, 1,", + " 0, -1, 1,", + " 0, 1, -1,", + " 0, -1, -1]);", + "const grad4 = new Float32Array([0, 1, 1, 1, 0, 1, 1, -1, 0, 1, -1, 1, 0, 1, -1, -1,", + " 0, -1, 1, 1, 0, -1, 1, -1, 0, -1, -1, 1, 0, -1, -1, -1,", + " 1, 0, 1, 1, 1, 0, 1, -1, 1, 0, -1, 1, 1, 0, -1, -1,", + " -1, 0, 1, 1, -1, 0, 1, -1, -1, 0, -1, 1, -1, 0, -1, -1,", + " 1, 1, 0, 1, 1, 1, 0, -1, 1, -1, 0, 1, 1, -1, 0, -1,", + " -1, 1, 0, 1, -1, 1, 0, -1, -1, -1, 0, 1, -1, -1, 0, -1,", + " 1, 1, 1, 0, 1, 1, -1, 0, 1, -1, 1, 0, 1, -1, -1, 0,", + " -1, 1, 1, 0, -1, 1, -1, 0, -1, -1, 1, 0, -1, -1, -1, 0]);", + "", + "", + "/**", + " * Builds a random permutation table.", + " * This is exported only for (internal) testing purposes.", + " * Do not rely on this export.", + " * @param {() => number} random", + " * @private", + " */", + "function buildPermutationTable(random) {", + " const p = new Uint8Array(256);", + " for (let i = 0; i < 256; i++) {", + " p[i] = i;", + " }", + " for (let i = 0; i < 255; i++) {", + " const r = i + ~~(random() * (256 - i));", + " const aux = p[i];", + " p[i] = p[r];", + " p[r] = aux;", + " }", + " return p;", + "}", + "", + "/*", + "The ALEA PRNG and masher code used by simplex-noise.js", + "is based on code by Johannes Baagøe, modified by Jonas Wagner.", + "See alea.md for the full license.", + "@param {string|number} seed", + "*/", + "function alea(seed) {", + " let s0 = 0;", + " let s1 = 0;", + " let s2 = 0;", + " let c = 1;", + " const mash = masher();", + " s0 = mash(' ');", + " s1 = mash(' ');", + " s2 = mash(' ');", + " s0 -= mash(seed);", + " if (s0 < 0) {", + " s0 += 1;", + " }", + " s1 -= mash(seed);", + " if (s1 < 0) {", + " s1 += 1;", + " }", + " s2 -= mash(seed);", + " if (s2 < 0) {", + " s2 += 1;", + " }", + " return function () {", + " const t = 2091639 * s0 + c * 2.3283064365386963e-10; // 2^-32", + " s0 = s1;", + " s1 = s2;", + " return s2 = t - (c = t | 0);", + " };", + "}", + "", + "function masher() {", + " let n = 0xefc8249d;", + " return function (data) {", + " data = data.toString();", + " for (let i = 0; i < data.length; i++) {", + " n += data.charCodeAt(i);", + " let h = 0.02519603282416938 * n;", + " n = h >>> 0;", + " h -= n;", + " h *= n;", + " n = h >>> 0;", + " h -= n;", + " n += h * 0x100000000; // 2^32", + " }", + " return (n >>> 0) * 2.3283064365386963e-10; // 2^-32", + " };", + "}", + "", + "/** Deterministic simplex noise generator suitable for 2D, 3D and 4D spaces. */", + "class SimplexNoise {", + " /**", + " * Creates a new `SimplexNoise` instance.", + " * This involves some setup. You can save a few cpu cycles by reusing the same instance.", + " * @param {(() => number)|string|number} randomOrSeed A random number generator or a seed (string|number).", + " * Defaults to Math.random (random irreproducible initialization).", + " */", + " constructor(randomOrSeed) {", + " if (randomOrSeed === void 0) { randomOrSeed = Math.random; }", + " const random = typeof randomOrSeed == 'function' ? randomOrSeed : alea(randomOrSeed);", + " this.p = buildPermutationTable(random);", + " this.perm = new Uint8Array(512);", + " this.permMod12 = new Uint8Array(512);", + " for (let i = 0; i < 512; i++) {", + " this.perm[i] = this.p[i & 255];", + " this.permMod12[i] = this.perm[i] % 12;", + " }", + " }", + "", + " /**", + " * Samples the noise field in 2 dimensions", + " * @param {number} x", + " * @param {number} y", + " * @returns a number in the interval [-1, 1]", + " */", + " noise2D(x, y) {", + " const permMod12 = this.permMod12;", + " const perm = this.perm;", + " let n0 = 0; // Noise contributions from the three corners", + " let n1 = 0;", + " let n2 = 0;", + " // Skew the input space to determine which simplex cell we're in", + " const s = (x + y) * F2; // Hairy factor for 2D", + " const i = Math.floor(x + s);", + " const j = Math.floor(y + s);", + " const t = (i + j) * G2;", + " const X0 = i - t; // Unskew the cell origin back to (x,y) space", + " const Y0 = j - t;", + " const x0 = x - X0; // The x,y distances from the cell origin", + " const y0 = y - Y0;", + " // For the 2D case, the simplex shape is an equilateral triangle.", + " // Determine which simplex we are in.", + " let i1, j1; // Offsets for second (middle) corner of simplex in (i,j) coords", + " if (x0 > y0) {", + " i1 = 1;", + " j1 = 0;", + " } // lower triangle, XY order: (0,0)->(1,0)->(1,1)", + " else {", + " i1 = 0;", + " j1 = 1;", + " } // upper triangle, YX order: (0,0)->(0,1)->(1,1)", + " // A step of (1,0) in (i,j) means a step of (1-c,-c) in (x,y), and", + " // a step of (0,1) in (i,j) means a step of (-c,1-c) in (x,y), where", + " // c = (3-sqrt(3))/6", + " const x1 = x0 - i1 + G2; // Offsets for middle corner in (x,y) unskewed coords", + " const y1 = y0 - j1 + G2;", + " const x2 = x0 - 1.0 + 2.0 * G2; // Offsets for last corner in (x,y) unskewed coords", + " const y2 = y0 - 1.0 + 2.0 * G2;", + " // Work out the hashed gradient indices of the three simplex corners", + " const ii = i & 255;", + " const jj = j & 255;", + " // Calculate the contribution from the three corners", + " let t0 = 0.5 - x0 * x0 - y0 * y0;", + " if (t0 >= 0) {", + " const gi0 = permMod12[ii + perm[jj]] * 3;", + " t0 *= t0;", + " n0 = t0 * t0 * (grad3[gi0] * x0 + grad3[gi0 + 1] * y0); // (x,y) of grad3 used for 2D gradient", + " }", + " let t1 = 0.5 - x1 * x1 - y1 * y1;", + " if (t1 >= 0) {", + " const gi1 = permMod12[ii + i1 + perm[jj + j1]] * 3;", + " t1 *= t1;", + " n1 = t1 * t1 * (grad3[gi1] * x1 + grad3[gi1 + 1] * y1);", + " }", + " let t2 = 0.5 - x2 * x2 - y2 * y2;", + " if (t2 >= 0) {", + " const gi2 = permMod12[ii + 1 + perm[jj + 1]] * 3;", + " t2 *= t2;", + " n2 = t2 * t2 * (grad3[gi2] * x2 + grad3[gi2 + 1] * y2);", + " }", + " // Add contributions from each corner to get the final noise value.", + " // The result is scaled to return values in the interval [-1,1].", + " return 70.0 * (n0 + n1 + n2);", + " }", + "", + " /**", + " * Samples the noise field in 3 dimensions", + " * @param {number} x", + " * @param {number} y", + " * @param {number} z", + " * @returns a number in the interval [-1, 1]", + " */", + " noise3D(x, y, z) {", + " const permMod12 = this.permMod12;", + " const perm = this.perm;", + " let n0, n1, n2, n3; // Noise contributions from the four corners", + " // Skew the input space to determine which simplex cell we're in", + " const s = (x + y + z) * F3; // Very nice and simple skew factor for 3D", + " const i = Math.floor(x + s);", + " const j = Math.floor(y + s);", + " const k = Math.floor(z + s);", + " const t = (i + j + k) * G3;", + " const X0 = i - t; // Unskew the cell origin back to (x,y,z) space", + " const Y0 = j - t;", + " const Z0 = k - t;", + " const x0 = x - X0; // The x,y,z distances from the cell origin", + " const y0 = y - Y0;", + " const z0 = z - Z0;", + " // For the 3D case, the simplex shape is a slightly irregular tetrahedron.", + " // Determine which simplex we are in.", + " let i1, j1, k1; // Offsets for second corner of simplex in (i,j,k) coords", + " let i2, j2, k2; // Offsets for third corner of simplex in (i,j,k) coords", + " if (x0 >= y0) {", + " if (y0 >= z0) {", + " i1 = 1;", + " j1 = 0;", + " k1 = 0;", + " i2 = 1;", + " j2 = 1;", + " k2 = 0;", + " } // X Y Z order", + " else if (x0 >= z0) {", + " i1 = 1;", + " j1 = 0;", + " k1 = 0;", + " i2 = 1;", + " j2 = 0;", + " k2 = 1;", + " } // X Z Y order", + " else {", + " i1 = 0;", + " j1 = 0;", + " k1 = 1;", + " i2 = 1;", + " j2 = 0;", + " k2 = 1;", + " } // Z X Y order", + " }", + " else { // x0 y0)", + " rankx++;", + " else", + " ranky++;", + " if (x0 > z0)", + " rankx++;", + " else", + " rankz++;", + " if (x0 > w0)", + " rankx++;", + " else", + " rankw++;", + " if (y0 > z0)", + " ranky++;", + " else", + " rankz++;", + " if (y0 > w0)", + " ranky++;", + " else", + " rankw++;", + " if (z0 > w0)", + " rankz++;", + " else", + " rankw++;", + " // simplex[c] is a 4-vector with the numbers 0, 1, 2 and 3 in some order.", + " // Many values of c will never occur, since e.g. x>y>z>w makes x= 3 ? 1 : 0;", + " const j1 = ranky >= 3 ? 1 : 0;", + " const k1 = rankz >= 3 ? 1 : 0;", + " const l1 = rankw >= 3 ? 1 : 0;", + " // The integer offsets for the third simplex corner", + " const i2 = rankx >= 2 ? 1 : 0;", + " const j2 = ranky >= 2 ? 1 : 0;", + " const k2 = rankz >= 2 ? 1 : 0;", + " const l2 = rankw >= 2 ? 1 : 0;", + " // The integer offsets for the fourth simplex corner", + " const i3 = rankx >= 1 ? 1 : 0;", + " const j3 = ranky >= 1 ? 1 : 0;", + " const k3 = rankz >= 1 ? 1 : 0;", + " const l3 = rankw >= 1 ? 1 : 0;", + " // The fifth corner has all coordinate offsets = 1, so no need to compute that.", + " const x1 = x0 - i1 + G4; // Offsets for second corner in (x,y,z,w) coords", + " const y1 = y0 - j1 + G4;", + " const z1 = z0 - k1 + G4;", + " const w1 = w0 - l1 + G4;", + " const x2 = x0 - i2 + 2.0 * G4; // Offsets for third corner in (x,y,z,w) coords", + " const y2 = y0 - j2 + 2.0 * G4;", + " const z2 = z0 - k2 + 2.0 * G4;", + " const w2 = w0 - l2 + 2.0 * G4;", + " const x3 = x0 - i3 + 3.0 * G4; // Offsets for fourth corner in (x,y,z,w) coords", + " const y3 = y0 - j3 + 3.0 * G4;", + " const z3 = z0 - k3 + 3.0 * G4;", + " const w3 = w0 - l3 + 3.0 * G4;", + " const x4 = x0 - 1.0 + 4.0 * G4; // Offsets for last corner in (x,y,z,w) coords", + " const y4 = y0 - 1.0 + 4.0 * G4;", + " const z4 = z0 - 1.0 + 4.0 * G4;", + " const w4 = w0 - 1.0 + 4.0 * G4;", + " // Work out the hashed gradient indices of the five simplex corners", + " const ii = i & 255;", + " const jj = j & 255;", + " const kk = k & 255;", + " const ll = l & 255;", + " // Calculate the contribution from the five corners", + " let t0 = 0.6 - x0 * x0 - y0 * y0 - z0 * z0 - w0 * w0;", + " if (t0 < 0)", + " n0 = 0.0;", + " else {", + " const gi0 = (perm[ii + perm[jj + perm[kk + perm[ll]]]] % 32) * 4;", + " t0 *= t0;", + " n0 = t0 * t0 * (grad4[gi0] * x0 + grad4[gi0 + 1] * y0 + grad4[gi0 + 2] * z0 + grad4[gi0 + 3] * w0);", + " }", + " let t1 = 0.6 - x1 * x1 - y1 * y1 - z1 * z1 - w1 * w1;", + " if (t1 < 0)", + " n1 = 0.0;", + " else {", + " const gi1 = (perm[ii + i1 + perm[jj + j1 + perm[kk + k1 + perm[ll + l1]]]] % 32) * 4;", + " t1 *= t1;", + " n1 = t1 * t1 * (grad4[gi1] * x1 + grad4[gi1 + 1] * y1 + grad4[gi1 + 2] * z1 + grad4[gi1 + 3] * w1);", + " }", + " let t2 = 0.6 - x2 * x2 - y2 * y2 - z2 * z2 - w2 * w2;", + " if (t2 < 0)", + " n2 = 0.0;", + " else {", + " const gi2 = (perm[ii + i2 + perm[jj + j2 + perm[kk + k2 + perm[ll + l2]]]] % 32) * 4;", + " t2 *= t2;", + " n2 = t2 * t2 * (grad4[gi2] * x2 + grad4[gi2 + 1] * y2 + grad4[gi2 + 2] * z2 + grad4[gi2 + 3] * w2);", + " }", + " let t3 = 0.6 - x3 * x3 - y3 * y3 - z3 * z3 - w3 * w3;", + " if (t3 < 0)", + " n3 = 0.0;", + " else {", + " const gi3 = (perm[ii + i3 + perm[jj + j3 + perm[kk + k3 + perm[ll + l3]]]] % 32) * 4;", + " t3 *= t3;", + " n3 = t3 * t3 * (grad4[gi3] * x3 + grad4[gi3 + 1] * y3 + grad4[gi3 + 2] * z3 + grad4[gi3 + 3] * w3);", + " }", + " let t4 = 0.6 - x4 * x4 - y4 * y4 - z4 * z4 - w4 * w4;", + " if (t4 < 0)", + " n4 = 0.0;", + " else {", + " const gi4 = (perm[ii + 1 + perm[jj + 1 + perm[kk + 1 + perm[ll + 1]]]] % 32) * 4;", + " t4 *= t4;", + " n4 = t4 * t4 * (grad4[gi4] * x4 + grad4[gi4 + 1] * y4 + grad4[gi4 + 2] * z4 + grad4[gi4 + 3] * w4);", + " }", + " // Sum up and scale the result to cover the range [-1,1]", + " return 27.0 * (n0 + n1 + n2 + n3 + n4);", + " };", + "}", + "", + "gdjs._shakeObjectExtension = {", + " noiseManager: new NoiseManager(),", + "};", + "" + ], + "parameterObjects": "", + "useStrict": true, + "eventsSheetExpanded": true + } + ], + "parameters": [], + "objectGroups": [] + }, + { + "description": "Generate a number from 2 dimensional simplex noise.", + "fullName": "2D noise", + "functionType": "Expression", + "name": "Noise2d", + "private": true, + "sentence": "", + "events": [ + { + "type": "BuiltinCommonInstructions::JsCode", + "inlineCode": [ + "const name = eventsFunctionContext.getArgument(\"Name\");\r", + "const x = eventsFunctionContext.getArgument(\"X\");\r", + "const y = eventsFunctionContext.getArgument(\"Y\");\r", + "\r", + "eventsFunctionContext.returnValue = gdjs._shakeObjectExtension.noiseManager.getGenerator(name).noise(x, y);" + ], + "parameterObjects": "", + "useStrict": true, + "eventsSheetExpanded": true + } + ], + "expressionType": { + "type": "expression" + }, + "parameters": [ + { + "description": "Generator name", + "name": "Name", + "type": "string" + }, + { + "description": "X coordinate", + "name": "X", + "type": "expression" + }, + { + "description": "Y coordinate", + "name": "Y", + "type": "expression" + } + ], + "objectGroups": [] + }, + { + "description": "Generate a number from 3 dimensional simplex noise.", + "fullName": "3D noise", + "functionType": "Expression", + "name": "Noise3d", + "private": true, + "sentence": "", + "events": [ + { + "type": "BuiltinCommonInstructions::JsCode", + "inlineCode": [ + "const name = eventsFunctionContext.getArgument(\"Name\");\r", + "const x = eventsFunctionContext.getArgument(\"X\");\r", + "const y = eventsFunctionContext.getArgument(\"Y\");\r", + "const z = eventsFunctionContext.getArgument(\"Z\");\r", + "\r", + "eventsFunctionContext.returnValue = gdjs._shakeObjectExtension.noiseManager.getGenerator(name).noise(x, y, z);" + ], + "parameterObjects": "", + "useStrict": true, + "eventsSheetExpanded": true + } + ], + "expressionType": { + "type": "expression" + }, + "parameters": [ + { + "description": "Generator name", + "name": "Name", + "type": "string" + }, + { + "description": "X coordinate", + "name": "X", + "type": "expression" + }, + { + "description": "Y coordinate", + "name": "Y", + "type": "expression" + }, + { + "description": "Z coordinate", + "name": "Z", + "type": "expression" + } + ], + "objectGroups": [] + }, + { + "description": "Generate a number from 4 dimensional simplex noise.", + "fullName": "4D noise", + "functionType": "Expression", + "name": "Noise4d", + "private": true, + "sentence": "", + "events": [ + { + "type": "BuiltinCommonInstructions::JsCode", + "inlineCode": [ + "const name = eventsFunctionContext.getArgument(\"Name\");\r", + "const x = eventsFunctionContext.getArgument(\"X\");\r", + "const y = eventsFunctionContext.getArgument(\"Y\");\r", + "const z = eventsFunctionContext.getArgument(\"Z\");\r", + "const w = eventsFunctionContext.getArgument(\"W\");\r", + "\r", + "eventsFunctionContext.returnValue = gdjs._shakeObjectExtension.noiseManager.getGenerator(name).noise(x, y, z, w);" + ], + "parameterObjects": "", + "useStrict": true, + "eventsSheetExpanded": true + } + ], + "expressionType": { + "type": "expression" + }, + "parameters": [ + { + "description": "Generator name", + "name": "Name", + "type": "string" + }, + { + "description": "X coordinate", + "name": "X", + "type": "expression" + }, + { + "description": "Y coordinate", + "name": "Y", + "type": "expression" + }, + { + "description": "Z coordinate", + "name": "Z", + "type": "expression" + }, + { + "description": "W coordinate", + "name": "W", + "type": "expression" + } + ], + "objectGroups": [] + }, + { + "description": "Create a noise generator with default settings (frequency = 1, octaves = 1, persistence = 0.5, lacunarity = 2).", + "fullName": "Create a noise generator", + "functionType": "Action", + "name": "Create", + "private": true, + "sentence": "Create a noise generator named _PARAM1_", + "events": [ + { + "type": "BuiltinCommonInstructions::JsCode", + "inlineCode": [ + "const name = eventsFunctionContext.getArgument(\"Name\");\r", + "\r", + "gdjs._shakeObjectExtension.noiseManager.getGenerator(name);" + ], + "parameterObjects": "", + "useStrict": true, + "eventsSheetExpanded": false + } + ], + "parameters": [ + { + "description": "Generator name", + "name": "Name", + "type": "string" + } + ], + "objectGroups": [] + }, + { + "description": "Delete a noise generator and loose its settings.", + "fullName": "Delete a noise generator", + "functionType": "Action", + "name": "Delete", + "private": true, + "sentence": "Delete _PARAM1_ noise generator", + "events": [ + { + "type": "BuiltinCommonInstructions::JsCode", + "inlineCode": [ + "const name = eventsFunctionContext.getArgument(\"Name\");\r", + "\r", + "gdjs._shakeObjectExtension.noiseManager.deleteGenerator(name);" + ], + "parameterObjects": "", + "useStrict": true, + "eventsSheetExpanded": false + } + ], + "parameters": [ + { + "description": "Generator name", + "name": "Name", + "type": "string" + } + ], + "objectGroups": [] + }, + { + "description": "Delete all noise generators and loose their settings.", + "fullName": "Delete all noise generators", + "functionType": "Action", + "name": "DeleteAll", + "private": true, + "sentence": "Delete all noise generators", + "events": [ + { + "type": "BuiltinCommonInstructions::JsCode", + "inlineCode": "gdjs._shakeObjectExtension.noiseManager.deleteAllGenerators();", + "parameterObjects": "", + "useStrict": true, + "eventsSheetExpanded": false + } + ], + "parameters": [], + "objectGroups": [] + }, + { + "description": "The seed is a number used to generate the random noise. Setting the same seed will result in the same random noise generation. It's for example useful to generate the same world, by saving this seed value and reusing it later to generate again a world.", + "fullName": "Noise seed", + "functionType": "Action", + "name": "SetSeed", + "private": true, + "sentence": "Change the noise seed to _PARAM1_", + "events": [ + { + "type": "BuiltinCommonInstructions::JsCode", + "inlineCode": "gdjs._shakeObjectExtension.noiseManager.setSeed(eventsFunctionContext.getArgument(\"Seed\"));", + "parameterObjects": "", + "useStrict": true, + "eventsSheetExpanded": false + } + ], + "parameters": [ + { + "description": "Seed", + "longDescription": "15 digits numbers maximum", + "name": "Seed", + "type": "expression" + } + ], + "objectGroups": [] + }, + { + "description": "Change the looping period on X used for noise generation. The noise will wrap-around on X.", + "fullName": "Noise looping period on X", + "functionType": "Action", + "name": "SetLoopPeriodX", + "private": true, + "sentence": "Change the looping period on X of _PARAM2_: _PARAM1_", + "events": [ + { + "type": "BuiltinCommonInstructions::JsCode", + "inlineCode": [ + "const name = eventsFunctionContext.getArgument(\"Name\");\r", + "\r", + "gdjs._shakeObjectExtension.noiseManager.getGenerator(name).xLoopPeriod = eventsFunctionContext.getArgument(\"LoopPeriod\");" + ], + "parameterObjects": "", + "useStrict": true, + "eventsSheetExpanded": false + } + ], + "parameters": [ + { + "description": "Looping period on X", + "name": "LoopPeriod", + "type": "expression" + }, + { + "description": "Generator name", + "name": "Name", + "type": "string" + } + ], + "objectGroups": [] + }, + { + "description": "Change the looping period on Y used for noise generation. The noise will wrap-around on Y.", + "fullName": "Noise looping period on Y", + "functionType": "Action", + "name": "SetLoopPeriodY", + "private": true, + "sentence": "Change the looping period on Y of _PARAM2_: _PARAM1_", + "events": [ + { + "type": "BuiltinCommonInstructions::JsCode", + "inlineCode": [ + "const name = eventsFunctionContext.getArgument(\"Name\");\r", + "\r", + "gdjs._shakeObjectExtension.noiseManager.getGenerator(name).yLoopPeriod = eventsFunctionContext.getArgument(\"LoopPeriod\");" + ], + "parameterObjects": "", + "useStrict": true, + "eventsSheetExpanded": true + } + ], + "parameters": [ + { + "description": "Looping period on Y", + "name": "LoopPeriod", + "type": "expression" + }, + { + "description": "Generator name", + "name": "Name", + "type": "string" + } + ], + "objectGroups": [] + }, + { + "description": "Change the base frequency used for noise generation. A lower frequency will zoom in the noise.", + "fullName": "Noise base frequency", + "functionType": "Action", + "name": "SetFrequency", + "private": true, + "sentence": "Change the noise frequency of _PARAM2_: _PARAM1_", + "events": [ + { + "type": "BuiltinCommonInstructions::JsCode", + "inlineCode": [ + "const name = eventsFunctionContext.getArgument(\"Name\");\r", + "\r", + "gdjs._shakeObjectExtension.noiseManager.getGenerator(name).frequency = eventsFunctionContext.getArgument(\"Frequency\");" + ], + "parameterObjects": "", + "useStrict": true, + "eventsSheetExpanded": false + } + ], + "parameters": [ + { + "description": "Frequency", + "name": "Frequency", + "type": "expression" + }, + { + "description": "Generator name", + "name": "Name", + "type": "string" + } + ], + "objectGroups": [] + }, + { + "description": "Change the number of octaves used for noise generation. It can be seen as layers of noise with different zoom.", + "fullName": "Noise octaves", + "functionType": "Action", + "name": "SetOctaves", + "private": true, + "sentence": "Change the number of noise octaves of _PARAM2_: _PARAM1_", + "events": [ + { + "type": "BuiltinCommonInstructions::JsCode", + "inlineCode": [ + "const name = eventsFunctionContext.getArgument(\"Name\");\r", + "\r", + "gdjs._shakeObjectExtension.noiseManager.getGenerator(name).octaves = eventsFunctionContext.getArgument(\"Octaves\");" + ], + "parameterObjects": "", + "useStrict": true, + "eventsSheetExpanded": false + } + ], + "parameters": [ + { + "description": "Octaves", + "name": "Octaves", + "type": "expression" + }, + { + "description": "Generator name", + "name": "Name", + "type": "string" + } + ], + "objectGroups": [] + }, + { + "description": "Change the persistence used for noise generation. At its default value \"0.5\", it halves the noise amplitude at each octave.", + "fullName": "Noise persistence", + "functionType": "Action", + "name": "SetPersistence", + "private": true, + "sentence": "Change the noise persistence of _PARAM2_: _PARAM1_", + "events": [ + { + "type": "BuiltinCommonInstructions::JsCode", + "inlineCode": [ + "const name = eventsFunctionContext.getArgument(\"Name\");\r", + "\r", + "gdjs._shakeObjectExtension.noiseManager.getGenerator(name).persistence = eventsFunctionContext.getArgument(\"Persistence\");" + ], + "parameterObjects": "", + "useStrict": true, + "eventsSheetExpanded": false + } + ], + "parameters": [ + { + "description": "Persistence", + "name": "Persistence", + "type": "expression" + }, + { + "description": "Generator name", + "name": "Name", + "type": "string" + } + ], + "objectGroups": [] + }, + { + "description": "Change the lacunarity used for noise generation. At its default value \"2\", it doubles the frequency at each octave.", + "fullName": "Noise lacunarity", + "functionType": "Action", + "name": "SetLacunarity", + "private": true, + "sentence": "Change the noise lacunarity of _PARAM2_: _PARAM1_", + "events": [ + { + "type": "BuiltinCommonInstructions::JsCode", + "inlineCode": [ + "const name = eventsFunctionContext.getArgument(\"Name\");\r", + "\r", + "gdjs._shakeObjectExtension.noiseManager.getGenerator(name).lacunarity = eventsFunctionContext.getArgument(\"Lacunarity\");" + ], + "parameterObjects": "", + "useStrict": true, + "eventsSheetExpanded": false + } + ], + "parameters": [ + { + "description": "Lacunarity", + "name": "Lacunarity", + "type": "expression" + }, + { + "description": "Generator name", + "name": "Name", + "type": "string" + } + ], + "objectGroups": [] + }, + { + "description": "The seed used for noise generation.", + "fullName": "Noise seed", + "functionType": "Expression", + "name": "Seed", + "private": true, + "sentence": "", + "events": [ + { + "type": "BuiltinCommonInstructions::JsCode", + "inlineCode": "eventsFunctionContext.returnValue = gdjs._shakeObjectExtension.noiseManager.seed;", + "parameterObjects": "", + "useStrict": true, + "eventsSheetExpanded": false + } + ], + "expressionType": { + "type": "expression" + }, + "parameters": [], + "objectGroups": [] + }, + { + "description": "The base frequency used for noise generation.", + "fullName": "Noise base frequency", + "functionType": "Expression", + "name": "Frequency", + "private": true, + "sentence": "", + "events": [ + { + "type": "BuiltinCommonInstructions::JsCode", + "inlineCode": [ + "const name = eventsFunctionContext.getArgument(\"Name\");\r", + "\r", + "eventsFunctionContext.returnValue = gdjs._shakeObjectExtension.noiseManager.getGenerator(name).frequency;" + ], + "parameterObjects": "", + "useStrict": true, + "eventsSheetExpanded": false + } + ], + "expressionType": { + "type": "expression" + }, + "parameters": [ + { + "description": "Generator name", + "name": "Name", + "type": "string" + } + ], + "objectGroups": [] + }, + { + "description": "The number of octaves used for noise generation.", + "fullName": "Noise octaves number", + "functionType": "Expression", + "name": "Octaves", + "private": true, + "sentence": "", + "events": [ + { + "type": "BuiltinCommonInstructions::JsCode", + "inlineCode": [ + "const name = eventsFunctionContext.getArgument(\"Name\");\r", + "\r", + "eventsFunctionContext.returnValue = gdjs._shakeObjectExtension.noiseManager.getGenerator(name).octaves;" + ], + "parameterObjects": "", + "useStrict": true, + "eventsSheetExpanded": false + } + ], + "expressionType": { + "type": "expression" + }, + "parameters": [ + { + "description": "Generator name", + "name": "Name", + "type": "string" + } + ], + "objectGroups": [] + }, + { + "description": "The persistence used for noise generation.", + "fullName": "Noise persistence", + "functionType": "Expression", + "name": "Persistence", + "private": true, + "sentence": "", + "events": [ + { + "type": "BuiltinCommonInstructions::JsCode", + "inlineCode": [ + "const name = eventsFunctionContext.getArgument(\"Name\");\r", + "\r", + "eventsFunctionContext.returnValue = gdjs._shakeObjectExtension.noiseManager.getGenerator(name).persistence;" + ], + "parameterObjects": "", + "useStrict": true, + "eventsSheetExpanded": false + } + ], + "expressionType": { + "type": "expression" + }, + "parameters": [ + { + "description": "Generator name", + "name": "Name", + "type": "string" + } + ], + "objectGroups": [] + }, + { + "description": "The lacunarity used for noise generation.", + "fullName": "Noise lacunarity", + "functionType": "Expression", + "name": "Lacunarity", + "private": true, + "sentence": "", + "events": [ + { + "type": "BuiltinCommonInstructions::JsCode", + "inlineCode": [ + "const name = eventsFunctionContext.getArgument(\"Name\");\r", + "\r", + "eventsFunctionContext.returnValue = gdjs._shakeObjectExtension.noiseManager.getGenerator(name).lacunarity;" + ], + "parameterObjects": "", + "useStrict": true, + "eventsSheetExpanded": false + } + ], + "expressionType": { + "type": "expression" + }, + "parameters": [ + { + "description": "Generator name", + "name": "Name", + "type": "string" + } + ], + "objectGroups": [] + } + ], + "eventsFunctionsFolderStructure": { + "folderName": "__ROOT", + "children": [ + { + "functionName": "onFirstSceneLoaded" + }, + { + "functionName": "DefineHelperClasses" + }, + { + "functionName": "Noise2d" + }, + { + "functionName": "Noise3d" + }, + { + "functionName": "Noise4d" + }, + { + "functionName": "Create" + }, + { + "functionName": "Delete" + }, + { + "functionName": "DeleteAll" + }, + { + "functionName": "SetSeed" + }, + { + "functionName": "SetLoopPeriodX" + }, + { + "functionName": "SetLoopPeriodY" + }, + { + "functionName": "SetFrequency" + }, + { + "functionName": "SetOctaves" + }, + { + "functionName": "SetPersistence" + }, + { + "functionName": "SetLacunarity" + }, + { + "functionName": "Seed" + }, + { + "functionName": "Frequency" + }, + { + "functionName": "Octaves" + }, + { + "functionName": "Persistence" + }, + { + "functionName": "Lacunarity" + } + ] + }, + "eventsBasedBehaviors": [ + { + "description": "Shake 3D objects with translation and rotation.", + "fullName": "3D shake", + "helpPath": "", + "iconUrl": "", + "name": "ShakeModel3D", + "objectType": "", + "previewIconUrl": "", + "quickCustomizationVisibility": "hidden", + "eventsFunctions": [ + { + "fullName": "", + "functionType": "Action", + "name": "onCreated", + "sentence": "", + "events": [ + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [ + { + "type": { + "value": "BooleanVariable" + }, + "parameters": [ + "IsStartingAtCreation", + "True", + "" + ] + } + ], + "actions": [ + { + "type": { + "value": "ShakeObject3D::DefineHelperClasses" + }, + "parameters": [ + "", + "" + ] + }, + { + "type": { + "value": "ShakeObject3D::ShakeModel3D::StartShaking" + }, + "parameters": [ + "Object", + "Behavior", + "0", + "" + ] + } + ] + } + ], + "parameters": [ + { + "description": "Object", + "name": "Object", + "type": "object" + }, + { + "description": "Behavior", + "name": "Behavior", + "supplementaryInformation": "ShakeObject3D::ShakeModel3D", + "type": "behavior" + } + ], + "objectGroups": [] + }, + { + "fullName": "", + "functionType": "Action", + "name": "doStepPostEvents", + "sentence": "", + "events": [ + { + "type": "BuiltinCommonInstructions::Comment", + "color": { + "b": 109, + "g": 230, + "r": 255, + "textB": 0, + "textG": 0, + "textR": 0 + }, + "comment": "Step time counters." + }, + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [], + "actions": [ + { + "type": { + "value": "SetNumberVariable" + }, + "parameters": [ + "Time", + "+", + "TimeDelta()" + ] + } + ] + }, + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [ + { + "type": { + "value": "ShakeObject3D::ShakeModel3D::IsShaking" + }, + "parameters": [ + "Object", + "Behavior", + "" + ] + } + ], + "actions": [ + { + "type": { + "value": "SetNumberVariable" + }, + "parameters": [ + "NoiseTime", + "+", + "TimeDelta() * Frequency" + ] + }, + { + "type": { + "value": "ShakeObject3D::ShakeModel3D::SetSharedPropertyEasingFactor" + }, + "parameters": [ + "Object", + "Behavior", + "=", + "min(Object.Behavior::StartEasingFactor(), Object.Behavior::StopEasingFactor())" + ] + } + ], + "events": [ + { + "type": "BuiltinCommonInstructions::Comment", + "color": { + "b": 109, + "g": 230, + "r": 255, + "textB": 0, + "textG": 0, + "textR": 0 + }, + "comment": "Shake the object.\nSave the object displacement to revert it in onScenePostEvents." + }, + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [ + { + "type": { + "value": "NumberVariable" + }, + "parameters": [ + "TranslationAmplitudeX", + "!=", + "0" + ] + } + ], + "actions": [ + { + "type": { + "value": "SetNumberVariable" + }, + "parameters": [ + "DeltaX", + "=", + "ShakeObject3D::Noise2d(\"\", NoiseTime, 1000) * TranslationAmplitudeX * EasingFactor" + ] + }, + { + "type": { + "value": "SetX" + }, + "parameters": [ + "Object", + "+", + "DeltaX" + ] + } + ] + }, + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [ + { + "type": { + "value": "NumberVariable" + }, + "parameters": [ + "TranslationAmplitudeY", + "!=", + "0" + ] + } + ], + "actions": [ + { + "type": { + "value": "SetNumberVariable" + }, + "parameters": [ + "DeltaY", + "=", + "ShakeObject3D::Noise2d(\"\", NoiseTime, 2000) * TranslationAmplitudeY * EasingFactor" + ] + }, + { + "type": { + "value": "SetY" + }, + "parameters": [ + "Object", + "+", + "DeltaY" + ] + } + ] + }, + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [ + { + "type": { + "value": "NumberVariable" + }, + "parameters": [ + "TranslationAmplitudeZ", + "!=", + "0" + ] + } + ], + "actions": [ + { + "type": { + "value": "SetNumberVariable" + }, + "parameters": [ + "DeltaZ", + "=", + "ShakeObject3D::Noise2d(\"\", NoiseTime, 3000) * TranslationAmplitudeZ * EasingFactor" + ] + }, + { + "type": { + "value": "Scene3D::Base3DBehavior::SetZ" + }, + "parameters": [ + "Object", + "Object3D", + "+", + "DeltaZ" + ] + } + ] + }, + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [ + { + "type": { + "value": "NumberVariable" + }, + "parameters": [ + "RotationAmplitudeX", + "!=", + "0" + ] + } + ], + "actions": [ + { + "type": { + "value": "SetNumberVariable" + }, + "parameters": [ + "DeltaAngleX", + "=", + "ShakeObject3D::Noise2d(\"\", NoiseTime, 4000) * RotationAmplitudeX * EasingFactor" + ] + }, + { + "type": { + "value": "Scene3D::Base3DBehavior::SetRotationX" + }, + "parameters": [ + "Object", + "Object3D", + "+", + "DeltaAngleX" + ] + } + ] + }, + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [ + { + "type": { + "value": "NumberVariable" + }, + "parameters": [ + "RotationAmplitudeY", + "!=", + "0" + ] + } + ], + "actions": [ + { + "type": { + "value": "SetNumberVariable" + }, + "parameters": [ + "DeltaAngleY", + "=", + "ShakeObject3D::Noise2d(\"\", NoiseTime, 5000) * RotationAmplitudeY * EasingFactor" + ] + }, + { + "type": { + "value": "Scene3D::Base3DBehavior::SetRotationY" + }, + "parameters": [ + "Object", + "Object3D", + "+", + "DeltaAngleY" + ] + } + ] + }, + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [ + { + "type": { + "value": "NumberVariable" + }, + "parameters": [ + "RotationAmplitudeZ", + "!=", + "0" + ] + } + ], + "actions": [ + { + "type": { + "value": "SetNumberVariable" + }, + "parameters": [ + "DeltaAngleZ", + "=", + "ShakeObject3D::Noise2d(\"\", NoiseTime, 6000) * RotationAmplitudeZ * EasingFactor" + ] + }, + { + "type": { + "value": "SetAngle" + }, + "parameters": [ + "Object", + "+", + "DeltaAngleZ" + ] + } + ] + } + ] + } + ], + "parameters": [ + { + "description": "Object", + "name": "Object", + "type": "object" + }, + { + "description": "Behavior", + "name": "Behavior", + "supplementaryInformation": "ShakeObject3D::ShakeModel3D", + "type": "behavior" + } + ], + "objectGroups": [] + }, + { + "fullName": "", + "functionType": "Action", + "name": "doStepPreEvents", + "sentence": "", + "events": [ + { + "type": "BuiltinCommonInstructions::Comment", + "color": { + "b": 109, + "g": 230, + "r": 255, + "textB": 0, + "textG": 0, + "textR": 0 + }, + "comment": "Revert the shaking." + }, + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [ + { + "type": { + "value": "ShakeObject3D::ShakeModel3D::IsShaking" + }, + "parameters": [ + "Object", + "Behavior", + "" + ] + } + ], + "actions": [], + "events": [ + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [ + { + "type": { + "value": "NumberVariable" + }, + "parameters": [ + "TranslationAmplitudeX", + "!=", + "0" + ] + } + ], + "actions": [ + { + "type": { + "value": "SetX" + }, + "parameters": [ + "Object", + "-", + "DeltaX" + ] + } + ] + }, + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [ + { + "type": { + "value": "NumberVariable" + }, + "parameters": [ + "TranslationAmplitudeY", + "!=", + "0" + ] + } + ], + "actions": [ + { + "type": { + "value": "SetY" + }, + "parameters": [ + "Object", + "-", + "DeltaY" + ] + } + ] + }, + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [ + { + "type": { + "value": "NumberVariable" + }, + "parameters": [ + "TranslationAmplitudeZ", + "!=", + "0" + ] + } + ], + "actions": [ + { + "type": { + "value": "Scene3D::Base3DBehavior::SetZ" + }, + "parameters": [ + "Object", + "Object3D", + "-", + "DeltaZ" + ] + } + ] + }, + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [ + { + "type": { + "value": "NumberVariable" + }, + "parameters": [ + "RotationAmplitudeX", + "!=", + "0" + ] + } + ], + "actions": [ + { + "type": { + "value": "Scene3D::Base3DBehavior::SetRotationX" + }, + "parameters": [ + "Object", + "Object3D", + "-", + "DeltaAngleX" + ] + } + ] + }, + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [ + { + "type": { + "value": "NumberVariable" + }, + "parameters": [ + "RotationAmplitudeY", + "!=", + "0" + ] + } + ], + "actions": [ + { + "type": { + "value": "Scene3D::Base3DBehavior::SetRotationY" + }, + "parameters": [ + "Object", + "Object3D", + "-", + "DeltaAngleY" + ] + } + ] + }, + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [ + { + "type": { + "value": "NumberVariable" + }, + "parameters": [ + "RotationAmplitudeZ", + "!=", + "0" + ] + } + ], + "actions": [ + { + "type": { + "value": "SetAngle" + }, + "parameters": [ + "Object", + "-", + "DeltaAngleZ" + ] + } + ] + } + ] + } + ], + "parameters": [ + { + "description": "Object", + "name": "Object", + "type": "object" + }, + { + "description": "Behavior", + "name": "Behavior", + "supplementaryInformation": "ShakeObject3D::ShakeModel3D", + "type": "behavior" + } + ], + "objectGroups": [] + }, + { + "description": "Shake the object with a linear easing at the start and the end.", + "fullName": "Shake", + "functionType": "Action", + "name": "Shake", + "sentence": "Shake _PARAM0_ for _PARAM2_ seconds with _PARAM3_ seconds of easing to start and _PARAM4_ seconds to stop", + "events": [ + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [], + "actions": [ + { + "type": { + "value": "SetNumberVariable" + }, + "parameters": [ + "Time", + "=", + "0" + ] + }, + { + "type": { + "value": "SetNumberVariable" + }, + "parameters": [ + "NoiseTime", + "=", + "TimeFromStart() * Frequency" + ] + }, + { + "type": { + "value": "SetNumberVariable" + }, + "parameters": [ + "Duration", + "=", + "NewDuration" + ] + }, + { + "type": { + "value": "SetNumberVariable" + }, + "parameters": [ + "StartEasingDuration", + "=", + "StartEaseDuration" + ] + }, + { + "type": { + "value": "SetNumberVariable" + }, + "parameters": [ + "StopEasingDuration", + "=", + "StopEaseDuration" + ] + } + ] + }, + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [ + { + "type": { + "value": "NumberVariable" + }, + "parameters": [ + "NewDuration", + "<", + "StartEaseDuration + StopEaseDuration" + ] + } + ], + "actions": [ + { + "type": { + "value": "SetNumberVariable" + }, + "parameters": [ + "StartEasingDuration", + "=", + "StartEaseDuration * NewDuration / (StartEaseDuration + StopEaseDuration)" + ] + }, + { + "type": { + "value": "SetNumberVariable" + }, + "parameters": [ + "StopEasingDuration", + "=", + "StopEaseDuration * NewDuration / (StartEaseDuration + StopEaseDuration)" + ] + } + ] + } + ], + "parameters": [ + { + "description": "Object", + "name": "Object", + "type": "object" + }, + { + "description": "Behavior", + "name": "Behavior", + "supplementaryInformation": "ShakeObject3D::ShakeModel3D", + "type": "behavior" + }, + { + "description": "Duration (in seconds)", + "name": "NewDuration", + "type": "expression" + }, + { + "description": "Ease duration to start (in seconds)", + "name": "StartEaseDuration", + "type": "expression" + }, + { + "description": "Ease duration to stop (in seconds)", + "name": "StopEaseDuration", + "type": "expression" + } + ], + "objectGroups": [] + }, + { + "description": "Shake the object with a linear easing at the start and keep shaking until the stop action is used.", + "fullName": "Start shaking", + "functionType": "Action", + "name": "StartShaking", + "sentence": "Start shaking _PARAM0_ with _PARAM2_ seconds of easing", + "events": [ + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [], + "actions": [ + { + "type": { + "value": "SetNumberVariable" + }, + "parameters": [ + "Time", + "=", + "0" + ] + }, + { + "type": { + "value": "SetNumberVariable" + }, + "parameters": [ + "NoiseTime", + "=", + "TimeFromStart() * Frequency" + ] + }, + { + "type": { + "value": "SetNumberVariable" + }, + "parameters": [ + "Duration", + "=", + "1234567890" + ] + }, + { + "type": { + "value": "SetNumberVariable" + }, + "parameters": [ + "StartEasingDuration", + "=", + "EaseDuration" + ] + } + ] + } + ], + "parameters": [ + { + "description": "Object", + "name": "Object", + "type": "object" + }, + { + "description": "Behavior", + "name": "Behavior", + "supplementaryInformation": "ShakeObject3D::ShakeModel3D", + "type": "behavior" + }, + { + "description": "Ease duration (in seconds)", + "name": "EaseDuration", + "type": "expression" + } + ], + "objectGroups": [] + }, + { + "description": "Stop shaking the object with a linear easing.", + "fullName": "Stop shaking", + "functionType": "Action", + "name": "StopShaking", + "sentence": "Stop shaking _PARAM0_ with _PARAM2_ seconds of easing", + "events": [ + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [ + { + "type": { + "value": "ShakeObject3D::ShakeModel3D::IsShaking" + }, + "parameters": [ + "Object", + "Behavior", + "" + ] + } + ], + "actions": [ + { + "type": { + "value": "SetNumberVariable" + }, + "parameters": [ + "Time", + "=", + "0" + ] + }, + { + "type": { + "value": "SetNumberVariable" + }, + "parameters": [ + "Duration", + "=", + "EaseDuration" + ] + }, + { + "type": { + "value": "SetNumberVariable" + }, + "parameters": [ + "StopEasingDuration", + "=", + "EaseDuration" + ] + } + ] + } + ], + "parameters": [ + { + "description": "Object", + "name": "Object", + "type": "object" + }, + { + "description": "Behavior", + "name": "Behavior", + "supplementaryInformation": "ShakeObject3D::ShakeModel3D", + "type": "behavior" + }, + { + "description": "Ease duration (in seconds)", + "name": "EaseDuration", + "type": "expression" + } + ], + "objectGroups": [] + }, + { + "description": "Check if the object is shaking.", + "fullName": "Is shaking", + "functionType": "Condition", + "name": "IsShaking", + "sentence": "_PARAM0_ is shaking", + "events": [ + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [ + { + "type": { + "value": "NumberVariable" + }, + "parameters": [ + "Time", + "<", + "Duration" + ] + }, + { + "type": { + "value": "NumberVariable" + }, + "parameters": [ + "Time", + ">", + "0" + ] + } + ], + "actions": [ + { + "type": { + "value": "SetReturnBoolean" + }, + "parameters": [ + "True" + ] + } + ] + } + ], + "parameters": [ + { + "description": "Object", + "name": "Object", + "type": "object" + }, + { + "description": "Behavior", + "name": "Behavior", + "supplementaryInformation": "ShakeObject3D::ShakeModel3D", + "type": "behavior" + } + ], + "objectGroups": [] + }, + { + "description": "Check if the object is stopping to shake.", + "fullName": "Is stopping to shake", + "functionType": "Condition", + "name": "IsStopping", + "sentence": "_PARAM0_ is stopping to shake", + "events": [ + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [ + { + "type": { + "value": "ShakeObject3D::ShakeModel3D::IsShaking" + }, + "parameters": [ + "Object", + "Behavior", + "" + ] + }, + { + "type": { + "value": "ShakeObject3D::ShakeModel3D::StopEasingFactor" + }, + "parameters": [ + "Object", + "Behavior", + "<", + "1", + "" + ] + } + ], + "actions": [ + { + "type": { + "value": "SetReturnBoolean" + }, + "parameters": [ + "True" + ] + } + ] + } + ], + "parameters": [ + { + "description": "Object", + "name": "Object", + "type": "object" + }, + { + "description": "Behavior", + "name": "Behavior", + "supplementaryInformation": "ShakeObject3D::ShakeModel3D", + "type": "behavior" + } + ], + "objectGroups": [] + }, + { + "description": "the shaking frequency of the object.", + "fullName": "Shaking frequency", + "functionType": "ExpressionAndCondition", + "group": "ShakeObject3D configuration", + "name": "Frequency", + "sentence": "the shaking frequency", + "events": [ + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [], + "actions": [ + { + "type": { + "value": "SetReturnNumber" + }, + "parameters": [ + "Frequency" + ] + } + ] + } + ], + "expressionType": { + "type": "expression" + }, + "parameters": [ + { + "description": "Object", + "name": "Object", + "type": "object" + }, + { + "description": "Behavior", + "name": "Behavior", + "supplementaryInformation": "ShakeObject3D::ShakeModel3D", + "type": "behavior" + } + ], + "objectGroups": [] + }, + { + "fullName": "", + "functionType": "ActionWithOperator", + "getterName": "Frequency", + "group": "ShakeObject3D configuration", + "name": "SetFrequency", + "sentence": "", + "events": [ + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [], + "actions": [ + { + "type": { + "value": "SetNumberVariable" + }, + "parameters": [ + "Frequency", + "=", + "Value" + ] + } + ] + } + ], + "parameters": [ + { + "description": "Object", + "name": "Object", + "type": "object" + }, + { + "description": "Behavior", + "name": "Behavior", + "supplementaryInformation": "ShakeObject3D::ShakeModel3D", + "type": "behavior" + } + ], + "objectGroups": [] + }, + { + "description": "Return the easing factor according to start properties.", + "fullName": "Start easing factor", + "functionType": "Expression", + "name": "StartEasingFactor", + "private": true, + "sentence": "", + "events": [ + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [ + { + "type": { + "value": "NumberVariable" + }, + "parameters": [ + "StartEasingDuration", + "<=", + "0" + ] + } + ], + "actions": [ + { + "type": { + "value": "SetReturnNumber" + }, + "parameters": [ + "1" + ] + } + ] + }, + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [ + { + "type": { + "value": "NumberVariable" + }, + "parameters": [ + "StartEasingDuration", + ">", + "0" + ] + } + ], + "actions": [ + { + "type": { + "value": "SetReturnNumber" + }, + "parameters": [ + "clamp(Time / StartEasingDuration, 0, 1)" + ] + } + ] + } + ], + "expressionType": { + "type": "expression" + }, + "parameters": [ + { + "description": "Object", + "name": "Object", + "type": "object" + }, + { + "description": "Behavior", + "name": "Behavior", + "supplementaryInformation": "ShakeObject3D::ShakeModel3D", + "type": "behavior" + } + ], + "objectGroups": [] + }, + { + "description": "Return the easing factor according to stop properties.", + "fullName": "Stop easing factor", + "functionType": "ExpressionAndCondition", + "name": "StopEasingFactor", + "private": true, + "sentence": "stop easing factor", + "events": [ + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [ + { + "type": { + "value": "NumberVariable" + }, + "parameters": [ + "StopEasingDuration", + "<=", + "0" + ] + } + ], + "actions": [], + "events": [ + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [ + { + "type": { + "value": "NumberVariable" + }, + "parameters": [ + "Time", + "<", + "Duration" + ] + } + ], + "actions": [ + { + "type": { + "value": "SetReturnNumber" + }, + "parameters": [ + "1" + ] + } + ] + }, + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [ + { + "type": { + "value": "NumberVariable" + }, + "parameters": [ + "Time", + ">=", + "Duration" + ] + } + ], + "actions": [ + { + "type": { + "value": "SetReturnNumber" + }, + "parameters": [ + "0" + ] + } + ] + } + ] + }, + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [ + { + "type": { + "value": "NumberVariable" + }, + "parameters": [ + "StopEasingDuration", + ">", + "0" + ] + } + ], + "actions": [ + { + "type": { + "value": "SetReturnNumber" + }, + "parameters": [ + "clamp((Duration - Time) / StopEasingDuration, 0, 1)" + ] + } + ] + } + ], + "expressionType": { + "type": "expression" + }, + "parameters": [ + { + "description": "Object", + "name": "Object", + "type": "object" + }, + { + "description": "Behavior", + "name": "Behavior", + "supplementaryInformation": "ShakeObject3D::ShakeModel3D", + "type": "behavior" + } + ], + "objectGroups": [] + } + ], + "eventsFunctionsFolderStructure": { + "folderName": "__ROOT", + "children": [ + { + "functionName": "onCreated" + }, + { + "functionName": "doStepPostEvents" + }, + { + "functionName": "doStepPreEvents" + }, + { + "functionName": "Shake" + }, + { + "functionName": "StartShaking" + }, + { + "functionName": "StopShaking" + }, + { + "functionName": "IsShaking" + }, + { + "functionName": "IsStopping" + }, + { + "folderName": "ShakeObject3D configuration", + "children": [ + { + "functionName": "Frequency" + }, + { + "functionName": "SetFrequency" + } + ] + }, + { + "functionName": "StartEasingFactor" + }, + { + "functionName": "StopEasingFactor" + } + ] + }, + "propertyDescriptors": [ + { + "value": "0", + "type": "Number", + "unit": "Pixel", + "label": "Translation amplitude on X axis", + "name": "TranslationAmplitudeX" + }, + { + "value": "0", + "type": "Number", + "unit": "Pixel", + "label": "Translation amplitude on Y axis", + "name": "TranslationAmplitudeY" + }, + { + "value": "0", + "type": "Number", + "unit": "Pixel", + "label": "Translation amplitude on Z axis", + "name": "TranslationAmplitudeZ" + }, + { + "value": "5", + "type": "Number", + "unit": "DegreeAngle", + "label": "Rotation amplitude around X axis", + "name": "RotationAmplitudeX" + }, + { + "value": "5", + "type": "Number", + "unit": "DegreeAngle", + "label": "Rotation amplitude around Y axis", + "name": "RotationAmplitudeY" + }, + { + "value": "5", + "type": "Number", + "unit": "DegreeAngle", + "label": "Rotation amplitude around Z axis", + "name": "RotationAmplitudeZ" + }, + { + "value": "", + "type": "Number", + "label": "", + "hidden": true, + "name": "Time" + }, + { + "value": "", + "type": "Number", + "label": "", + "hidden": true, + "name": "Duration" + }, + { + "value": "", + "type": "Number", + "label": "", + "hidden": true, + "name": "StartEasingDuration" + }, + { + "value": "", + "type": "Number", + "label": "", + "hidden": true, + "name": "StopEasingDuration" + }, + { + "value": "", + "type": "Number", + "label": "Frequency", + "name": "Frequency" + }, + { + "value": "", + "type": "Number", + "label": "", + "hidden": true, + "name": "DeltaX" + }, + { + "value": "", + "type": "Number", + "label": "", + "hidden": true, + "name": "DeltaY" + }, + { + "value": "", + "type": "Number", + "label": "", + "hidden": true, + "name": "DeltaZ" + }, + { + "value": "", + "type": "Number", + "label": "", + "hidden": true, + "name": "DeltaAngleX" + }, + { + "value": "", + "type": "Number", + "label": "", + "hidden": true, + "name": "DeltaAngleY" + }, + { + "value": "", + "type": "Number", + "label": "", + "hidden": true, + "name": "DeltaAngleZ" + }, + { + "value": "", + "type": "Number", + "label": "", + "hidden": true, + "name": "NoiseTime" + }, + { + "value": "", + "type": "Behavior", + "label": "3D capability", + "extraInformation": [ + "Scene3D::Base3DBehavior" + ], + "choices": [], + "name": "Object3D" + }, + { + "value": "", + "type": "Boolean", + "label": "Start to shake at the object creation", + "name": "IsStartingAtCreation" + } + ], + "propertiesFolderStructure": { + "folderName": "__ROOT", + "children": [ + { + "propertyName": "TranslationAmplitudeX" + }, + { + "propertyName": "TranslationAmplitudeY" + }, + { + "propertyName": "TranslationAmplitudeZ" + }, + { + "propertyName": "RotationAmplitudeX" + }, + { + "propertyName": "RotationAmplitudeY" + }, + { + "propertyName": "RotationAmplitudeZ" + }, + { + "propertyName": "Time" + }, + { + "propertyName": "Duration" + }, + { + "propertyName": "StartEasingDuration" + }, + { + "propertyName": "StopEasingDuration" + }, + { + "propertyName": "Frequency" + }, + { + "propertyName": "DeltaX" + }, + { + "propertyName": "DeltaY" + }, + { + "propertyName": "DeltaZ" + }, + { + "propertyName": "DeltaAngleX" + }, + { + "propertyName": "DeltaAngleY" + }, + { + "propertyName": "DeltaAngleZ" + }, + { + "propertyName": "NoiseTime" + }, + { + "propertyName": "Object3D" + }, + { + "propertyName": "IsStartingAtCreation" + } + ] + }, + "sharedPropertyDescriptors": [ + { + "value": "", + "type": "Number", + "label": "", + "hidden": true, + "name": "EasingFactor" + } + ], + "sharedPropertiesFolderStructure": { + "folderName": "__ROOT", + "children": [ + { + "propertyName": "EasingFactor" + } + ] + } + } + ], + "eventsBasedObjects": [] + }, + { + "author": "", + "category": "Visual effect", + "dimension": "3D", + "extensionNamespace": "", + "fullName": "3D particle emitter", + "gdevelopVersion": ">=5.5.222", + "helpPath": "", + "iconUrl": "data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iVVRGLTgiPz48IURPQ1RZUEUgc3ZnIFBVQkxJQyAiLS8vVzNDLy9EVEQgU1ZHIDEuMS8vRU4iICJodHRwOi8vd3d3LnczLm9yZy9HcmFwaGljcy9TVkcvMS4xL0RURC9zdmcxMS5kdGQiPjxzdmcgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIiB4bWxuczp4bGluaz0iaHR0cDovL3d3dy53My5vcmcvMTk5OS94bGluayIgdmVyc2lvbj0iMS4xIiBpZD0ibWRpLWZpcmUiIHdpZHRoPSIyNCIgaGVpZ2h0PSIyNCIgdmlld0JveD0iMCAwIDI0IDI0Ij48cGF0aCBkPSJNMTcuNjYgMTEuMkMxNy40MyAxMC45IDE3LjE1IDEwLjY0IDE2Ljg5IDEwLjM4QzE2LjIyIDkuNzggMTUuNDYgOS4zNSAxNC44MiA4LjcyQzEzLjMzIDcuMjYgMTMgNC44NSAxMy45NSAzQzEzIDMuMjMgMTIuMTcgMy43NSAxMS40NiA0LjMyQzguODcgNi40IDcuODUgMTAuMDcgOS4wNyAxMy4yMkM5LjExIDEzLjMyIDkuMTUgMTMuNDIgOS4xNSAxMy41NUM5LjE1IDEzLjc3IDkgMTMuOTcgOC44IDE0LjA1QzguNTcgMTQuMTUgOC4zMyAxNC4wOSA4LjE0IDEzLjkzQzguMDggMTMuODggOC4wNCAxMy44MyA4IDEzLjc2QzYuODcgMTIuMzMgNi42OSAxMC4yOCA3LjQ1IDguNjRDNS43OCAxMCA0Ljg3IDEyLjMgNSAxNC40N0M1LjA2IDE0Ljk3IDUuMTIgMTUuNDcgNS4yOSAxNS45N0M1LjQzIDE2LjU3IDUuNyAxNy4xNyA2IDE3LjdDNy4wOCAxOS40MyA4Ljk1IDIwLjY3IDEwLjk2IDIwLjkyQzEzLjEgMjEuMTkgMTUuMzkgMjAuOCAxNy4wMyAxOS4zMkMxOC44NiAxNy42NiAxOS41IDE1IDE4LjU2IDEyLjcyTDE4LjQzIDEyLjQ2QzE4LjIyIDEyIDE3LjY2IDExLjIgMTcuNjYgMTEuMk0xNC41IDE3LjVDMTQuMjIgMTcuNzQgMTMuNzYgMTggMTMuNCAxOC4xQzEyLjI4IDE4LjUgMTEuMTYgMTcuOTQgMTAuNSAxNy4yOEMxMS42OSAxNyAxMi40IDE2LjEyIDEyLjYxIDE1LjIzQzEyLjc4IDE0LjQzIDEyLjQ2IDEzLjc3IDEyLjMzIDEzQzEyLjIxIDEyLjI2IDEyLjIzIDExLjYzIDEyLjUgMTAuOTRDMTIuNjkgMTEuMzIgMTIuODkgMTEuNyAxMy4xMyAxMkMxMy45IDEzIDE1LjExIDEzLjQ0IDE1LjM3IDE0LjhDMTUuNDEgMTQuOTQgMTUuNDMgMTUuMDggMTUuNDMgMTUuMjNDMTUuNDYgMTYuMDUgMTUuMSAxNi45NSAxNC41IDE3LjVIMTQuNVoiIC8+PC9zdmc+", + "name": "ParticleEmitter3D", + "previewIconUrl": "https://asset-resources.gdevelop.io/public-resources/Icons/f2e5a34bf465f781866677762d385d6c8e9e8d203383f2df9a3b7e0fad6a2cb5_fire.svg", + "shortDescription": "Display a large number of particles in 3D to create visual effects in a 3D game.", + "version": "3.1.0", + "description": [ + "3D particle emitters let you create and display many small particles to simulate visual effects in your game — like fire, explosions, smoke, or dust.", + "", + "The parameters of the object can be configured in multiple different ways to create a specific desired effect. GDevelop will give a set of a pre-configured 3D particle emitters that you should try first.", + "", + "Use these 3D particle emitters in 3D games. For 2D games or particles that appear on a game's 2D user interface, use the 2D particle emitter object instead." + ], + "origin": { + "identifier": "ParticleEmitter3D", + "name": "gdevelop-extension-store" + }, + "tags": [ + "3d", + "particle", + "explosion", + "fire", + "smoke", + "splash", + "light" + ], + "authorIds": [ + "IWykYNRvhCZBN3vEgKEbBPOR3Oc2" + ], + "changelog": [ + { + "version": "3.0.0", + "breaking": "- the object rotates the other way around X axis." + }, + { + "version": "2.0.0", + "breaking": "- Object properties for position and rotation have been removed. They must be set with the instance editor or the action." + }, + { + "version": "1.0.0", + "breaking": "- Particles were 3 times too small." + } + ], + "dependencies": [], + "globalVariables": [], + "sceneVariables": [], + "eventsFunctions": [ + { + "description": "Define helper classes JavaScript code.", + "fullName": "Define helper classes", + "functionType": "Action", + "name": "DefineHelperClasses", + "private": true, + "sentence": "Define helper classes JavaScript code", + "events": [ + { + "type": "BuiltinCommonInstructions::JsCode", + "inlineCode": [ + "if (gdjs.__particleEmmiter3DExtension) {", + " return;", + "}", + "", + "const game = runtimeScene.getGame();", + "", + "class ParticleEmitter3DRenderer extends gdjs.CustomRuntimeObject3DRenderer {", + " constructor(", + " object,", + " instanceContainer,", + " parent", + " ) {", + " super(object, instanceContainer, parent);", + " }", + "", + " _updateThreeGroup() {", + " const threeObject3D = this.get3DRendererObject();", + "", + " threeObject3D.rotation.set(", + " gdjs.toRad(this._object.getRotationX()),", + " gdjs.toRad(this._object.getRotationY()),", + " gdjs.toRad(this._object.angle)", + " );", + "", + " threeObject3D.position.set(", + " this._object.getX(),", + " this._object.getY(),", + " this._object.getZ()", + " );", + "", + " // Force the scale to 1 because the particle emitter doesn't really has a size.", + " threeObject3D.scale.set(", + " this._object.isFlippedX() ? -1 : 1,", + " this._object.isFlippedY() ? -1 : 1,", + " this._object.isFlippedZ() ? -1 : 1", + " );", + "", + " threeObject3D.visible = !this._object.hidden;", + "", + " this._isContainerDirty = true;", + " }", + "}", + "", + "const coneLength = 64;", + "", + "class ParticleEmitterHelper extends THREE.Object3D {", + " /** @type {gdjs.CustomRuntimeObject3D} */", + " object;", + " /** @type {THREE.LineSegments} */", + " cone;", + " /** @type {THREE.LineSegments} */", + " centerLine;", + " /** @type {THREE.Mesh} */", + " originBox;", + "", + "\t/**", + " * @param gdjs.CustomRuntimeObject3D object", + "\t */", + " constructor(object) {", + " super();", + " this.object = object;", + " this.type = 'ParticleEmitterHelper';", + "", + " const coneGeometry = new THREE.BufferGeometry();", + " const conePositions = [", + " 0, 0, 0, 0, 0, 1,", + " 0, 0, 0, 1, 0, 1,", + " 0, 0, 0, - 1, 0, 1,", + " 0, 0, 0, 0, 1, 1,", + " 0, 0, 0, 0, - 1, 1", + " ];", + " for (let i = 0, j = 1, l = 32; i < l; i++ , j++) {", + " const p1 = (i / l) * Math.PI * 2;", + " const p2 = (j / l) * Math.PI * 2;", + " conePositions.push(", + " Math.cos(p1), Math.sin(p1), 1,", + " Math.cos(p2), Math.sin(p2), 1", + " );", + " }", + " coneGeometry.setAttribute('position', new THREE.Float32BufferAttribute(conePositions, 3));", + " this.cone = new THREE.LineSegments(", + " coneGeometry,", + " new THREE.LineBasicMaterial({ fog: false, toneMapped: false })", + " );", + " this.add(this.cone);", + " const centerLineGeometry = new THREE.BufferGeometry();", + " centerLineGeometry.setAttribute('position', new THREE.Float32BufferAttribute([", + " 0, 0, 0, 0, 0, coneLength", + " ], 3));", + " this.centerLine = new THREE.LineSegments(", + " centerLineGeometry,", + " new THREE.LineBasicMaterial({ fog: false, toneMapped: false })", + " );", + " this.add(this.centerLine);", + "", + " this.originBox = new THREE.Mesh(", + " new THREE.BoxGeometry(16, 16, 16),", + " new THREE.MeshBasicMaterial({ fog: false, toneMapped: false })", + " );", + " this.add(this.originBox);", + " this.update();", + "", + " this.originBox.gdjsRuntimeObject = object;", + " }", + "", + " dispose() {", + " this.cone.geometry.dispose();", + " this.cone.material.dispose();", + " this.centerLine.geometry.dispose();", + " this.centerLine.material.dispose();", + " this.originBox.geometry.dispose();", + " this.originBox.material.dispose();", + " }", + "", + " update() {", + " const coneWidth = coneLength * Math.sin(gdjs.toRad(this.object._getSpayConeAngle()));", + " const coneHeight = coneLength * Math.cos(gdjs.toRad(this.object._getSpayConeAngle()));", + " this.cone.scale.set(coneWidth, coneWidth, coneHeight);", + " const startColor = gdjs.rgbOrHexStringToNumber(this.object._getStartColor())", + " const endColor = gdjs.rgbOrHexStringToNumber(this.object._getEndColor());", + " this.cone.material.color.set(endColor);", + " this.centerLine.material.color.set(startColor);", + " this.originBox.material.color.set(startColor);", + " }", + "}", + "", + "/**", + " * @param {string} colorString", + " * @param {THREE.Vector4} threeColor", + " */", + "const setThreeColor = (colorString, threeColor = new THREE.Vector4()) => {", + " const integerColor = gdjs.rgbOrHexToRGBColor(colorString);", + " threeColor.x = integerColor[0] / 255;", + " threeColor.y = integerColor[1] / 255;", + " threeColor.z = integerColor[2] / 255;", + "};", + "", + "/**", + " * @param {string} integerOpacity", + " * @param {THREE.Vector4} threeColor", + " */", + "const setThreeOpacity = (integerOpacity, threeColor = new THREE.Vector4()) => {", + " threeColor.w = integerOpacity / 255;", + "};", + "", + "class ParticleEmitterAdapter {", + " /**", + " * @param particleSystem {ParticleSystem}", + " * @param colorOverLife {ColorOverLife}", + " * @param sizeOverLife {SizeOverLife}", + " * @param applyForce {ApplyForce}", + " * @param applyForce {ApplyForce}", + " * @param widthOverLength {WidthOverLength}", + " */", + " constructor(particleSystem, colorOverLife, sizeOverLife, applyForce, widthOverLength) {", + " this.particleSystem = particleSystem;", + " this.colorOverLife = colorOverLife;", + " this.sizeOverLife = sizeOverLife;", + " this.applyForce = applyForce;", + " this.widthOverLength = widthOverLength;", + " }", + "", + " /**", + " * @param renderMode {'Billboard' | 'Trail'}", + " */", + " setRenderMode(renderMode) {", + " switch (renderMode) {", + " case \"BillBoard\":", + " this.particleSystem.renderMode = RenderMode.BillBoard;", + " break;", + " case \"Trail\":", + " this.particleSystem.renderMode = RenderMode.Trail;", + " this.particleSystem.rendererEmitterSettings.startLength = new IntervalValue(30, 30);", + " break;", + " }", + " }", + "", + " /**", + " * @param startLengthMin {number}", + " */", + " setTrailStartLengthMin(startLengthMin) {", + " const startLength = this.particleSystem.rendererEmitterSettings.startLength;", + " if (!startLength) {", + " return;", + " }", + " startLength.a = startLengthMin;", + " }", + "", + " /**", + " * @param startLengthMax {number}", + " */", + " setTrailStartLengthMax(startLengthMax) {", + " const startLength = this.particleSystem.rendererEmitterSettings.startLength;", + " if (!startLength) {", + " return;", + " }", + " startLength.b = startLengthMax;", + " }", + "", + " /**", + " * @param followLocalOrigin {boolean}", + " */", + " setTrailFollowingLocalOrigin(followLocalOrigin) {", + " this.particleSystem.rendererEmitterSettings.followLocalOrigin = followLocalOrigin;", + " }", + "", + " /**", + " * @param trailEndWidthRatio {number}", + " */", + " setTrailEndWidthRatio(trailEndWidthRatio) {", + " // Check this comment in the lib code:", + " // We added a multiplication with size because it's more convenient for the extension.", + "", + " /** @type Bezier */", + " const bezier = this.widthOverLength.width.functions[0][0];", + " bezier.p[0] = 1;", + " bezier.p[1] = gdjs.evtTools.common.lerp(1, trailEndWidthRatio, 1 / 3);", + " bezier.p[2] = gdjs.evtTools.common.lerp(1, trailEndWidthRatio, 2 / 3);", + " bezier.p[3] = trailEndWidthRatio;", + " }", + "", + "", + " /**", + " * @param startColor {string}", + " */", + " setStartColor(startColor) {", + " setThreeColor(startColor, this.colorOverLife.color.color.keys[0][0]);", + " }", + "", + " /**", + " * @param endColor {string}", + " */", + " setEndColor(endColor) {", + " setThreeColor(endColor, this.colorOverLife.color.color.keys[1][0]);", + " }", + "", + " /**", + " * @param startOpacity {number}", + " */", + " setStartOpacity(startOpacity) {", + " this.colorOverLife.color.alpha.keys[0][0] = startOpacity / 255;", + " }", + "", + " /**", + " * @param endOpacity {number}", + " */", + " setEndOpacity(endOpacity) {", + " this.colorOverLife.color.alpha.keys[1][0] = endOpacity / 255;", + " }", + "", + " /**", + " * @param flow {number}", + " */", + " setFlow(flow) {", + " this.particleSystem.emissionOverTime.value = flow;", + " }", + "", + " /**", + " * @param startMinSize {number}", + " */", + " setStartMinSize(startMinSize) {", + " this.particleSystem.startSize.a = startMinSize;", + " }", + "", + " /**", + " * @param startMaxSize {number}", + " */", + " setStartMaxSize(startMaxSize) {", + " this.particleSystem.startSize.b = startMaxSize;", + " }", + "", + " /**", + " * @param endScale {number}", + " */", + " setEndScale(endScale) {", + " /** @type Bezier */", + " const bezier = this.sizeOverLife.size.functions[0][0];", + " bezier.p[0] = 1;", + " bezier.p[1] = gdjs.evtTools.common.lerp(1, endScale, 1 / 3);", + " bezier.p[2] = gdjs.evtTools.common.lerp(1, endScale, 2 / 3);", + " bezier.p[3] = endScale;", + " }", + "", + " /**", + " * @param startSpeedMin {number}", + " */", + " setStartSpeedMin(startSpeedMin) {", + " this.particleSystem.startSpeed.a = startSpeedMin;", + " }", + "", + " /**", + " * @param startSpeedMax {number}", + " */", + " setStartSpeedMax(startSpeedMax) {", + " this.particleSystem.startSpeed.b = startSpeedMax;", + " }", + "", + " /**", + " * @param lifespanMin {number}", + " */", + " setLifespanMin(lifespanMin) {", + " this.particleSystem.startLife.a = lifespanMin;", + " }", + "", + " /**", + " * @param lifespanMax {number}", + " */", + " setLifespanMax(lifespanMax) {", + " this.particleSystem.startLife.b = lifespanMax;", + " }", + "", + " /**", + " * @param duration {number}", + " */", + " setDuration(duration) {", + " this.particleSystem.duration = duration || Number.POSITIVE_INFINITY;", + " }", + "", + " /**", + " * @param areParticlesRelative {boolean}", + " */", + " setParticlesRelative(areParticlesRelative) {", + " this.particleSystem.worldSpace = !areParticlesRelative;", + " }", + "", + " /**", + " * @param sprayConeAngle {number}", + " */", + " setSprayConeAngle(sprayConeAngle) {", + " if (0 < sprayConeAngle && sprayConeAngle <= 180) {", + " if (!this.particleSystem.emitterShape.angle) {", + " this.particleSystem.emitterShape = new ConeEmitter({ radius: 0.1, angle: Math.PI / 8 });", + " }", + " this.particleSystem.emitterShape.angle = sprayConeAngle * Math.PI / 180;", + " }", + " else {", + " if (this.particleSystem.emitterShape.angle) {", + " this.particleSystem.emitterShape = new PointEmitter();", + " }", + " }", + " }", + "", + " /**", + " * @param blending {string}", + " */", + " setBlending(blendingString) {", + " const blending =", + " blendingString === \"Additive\" ? THREE.AdditiveBlending :", + " blendingString === \"Normal\" ? THREE.NormalBlending :", + " blendingString === \"Subtractive\" ? THREE.SubtractiveBlending :", + " blendingString === \"Multiply\" ? THREE.MultiplyBlending :", + " blendingString === \"None\" ? THREE.NoBlending :", + " THREE.AdditiveBlending;", + " // TODO This doesn't work.", + " this.particleSystem.blending = blending;", + " }", + "", + " /**", + " * @param gravity {number}", + " */", + " setGravity(gravity) {", + " this.applyForce.magnitude.value = gravity;", + " }", + "", + " /**", + " * @param gravityTop {string}", + " */", + " setGravityTop(gravityTop) {", + " // TODO Make gravity absolute even when \"relative\" is enabled. ", + " switch (gravityTop) {", + " case \"Z+\":", + " this.applyForce.direction.set(0, 0, -1);", + " break;", + "", + " case \"Y-\":", + " this.applyForce.direction.set(0, 1, 0);", + " break;", + " }", + " }", + "", + " setImage(resourceName) {", + " const texture = game.getImageManager().getThreeTexture(resourceName);", + " this.particleSystem.texture = texture;", + " }", + "}", + "", + "", + "", + "/**", + " * three.quarks v0.11.2 build Mon Jan 22 2024", + " * https://github.com/Alchemist0823/three.quarks#readme", + " * Copyright 2024 Alchemist0823 , MIT", + " */", + "", + "// The migration to addUpdateRange was backported without updating the library", + "// because GDevelop still uses Three.js 0.160.0.", + "", + "class ParticleEmitter extends THREE.Object3D {", + " constructor(system) {", + " super();", + " this.type = 'ParticleEmitter';", + " this.system = system;", + " }", + " clone() {", + " const system = this.system.clone();", + " system.emitter.copy(this, true);", + " return system.emitter;", + " }", + " dispose() { }", + " extractFromCache(cache) {", + " const values = [];", + " for (const key in cache) {", + " const data = cache[key];", + " delete data.metadata;", + " values.push(data);", + " }", + " return values;", + " }", + " toJSON(meta, options = {}) {", + " const isRootObject = meta === undefined || typeof meta === 'string';", + " const output = {};", + " if (isRootObject) {", + " meta = {", + " geometries: {},", + " materials: {},", + " textures: {},", + " images: {},", + " shapes: {},", + " skeletons: {},", + " animations: {},", + " nodes: {},", + " };", + " output.metadata = {", + " version: 4.5,", + " type: 'Object',", + " generator: 'Object3D.toJSON',", + " };", + " }", + " const object = {};", + " object.uuid = this.uuid;", + " object.type = this.type;", + " if (this.name !== '')", + " object.name = this.name;", + " if (this.castShadow === true)", + " object.castShadow = true;", + " if (this.receiveShadow === true)", + " object.receiveShadow = true;", + " if (this.visible === false)", + " object.visible = false;", + " if (this.frustumCulled === false)", + " object.frustumCulled = false;", + " if (this.renderOrder !== 0)", + " object.renderOrder = this.renderOrder;", + " if (JSON.stringify(this.userData) !== '{}')", + " object.userData = this.userData;", + " object.layers = this.layers.mask;", + " object.matrix = this.matrix.toArray();", + " if (this.matrixAutoUpdate === false)", + " object.matrixAutoUpdate = false;", + " if (this.system !== null)", + " object.ps = this.system.toJSON(meta, options);", + " if (this.children.length > 0) {", + " object.children = [];", + " for (let i = 0; i < this.children.length; i++) {", + " if (this.children[i].type !== 'ParticleSystemPreview') {", + " object.children.push(this.children[i].toJSON(meta).object);", + " }", + " }", + " }", + " if (isRootObject) {", + " const geometries = this.extractFromCache(meta.geometries);", + " const materials = this.extractFromCache(meta.materials);", + " const textures = this.extractFromCache(meta.textures);", + " const images = this.extractFromCache(meta.images);", + " if (geometries.length > 0)", + " output.geometries = geometries;", + " if (materials.length > 0)", + " output.materials = materials;", + " if (textures.length > 0)", + " output.textures = textures;", + " if (images.length > 0)", + " output.images = images;", + " }", + " output.object = object;", + " return output;", + " }", + "}", + "", + "class LinkedListNode {", + " constructor(data) {", + " this.data = data;", + " this.next = null;", + " this.prev = null;", + " }", + " hasPrev() {", + " return this.prev !== null;", + " }", + " hasNext() {", + " return this.next !== null;", + " }", + "}", + "class LinkedList {", + " constructor() {", + " this.length = 0;", + " this.head = this.tail = null;", + " }", + " isEmpty() {", + " return this.head === null;", + " }", + " clear() {", + " this.length = 0;", + " this.head = this.tail = null;", + " }", + " front() {", + " if (this.head === null)", + " return null;", + " return this.head.data;", + " }", + " back() {", + " if (this.tail === null)", + " return null;", + " return this.tail.data;", + " }", + " dequeue() {", + " if (this.head) {", + " const value = this.head.data;", + " this.head = this.head.next;", + " if (!this.head) {", + " this.tail = null;", + " }", + " else {", + " this.head.prev = null;", + " }", + " this.length--;", + " return value;", + " }", + " return undefined;", + " }", + " pop() {", + " if (this.tail) {", + " const value = this.tail.data;", + " this.tail = this.tail.prev;", + " if (!this.tail) {", + " this.head = null;", + " }", + " else {", + " this.tail.next = null;", + " }", + " this.length--;", + " return value;", + " }", + " return undefined;", + " }", + " queue(data) {", + " const node = new LinkedListNode(data);", + " if (!this.tail) {", + " this.tail = node;", + " }", + " if (this.head) {", + " this.head.prev = node;", + " node.next = this.head;", + " }", + " this.head = node;", + " this.length++;", + " }", + " push(data) {", + " const node = new LinkedListNode(data);", + " if (!this.head) {", + " this.head = node;", + " }", + " if (this.tail) {", + " this.tail.next = node;", + " node.prev = this.tail;", + " }", + " this.tail = node;", + " this.length++;", + " }", + " insertBefore(node, data) {", + " const newNode = new LinkedListNode(data);", + " newNode.next = node;", + " newNode.prev = node.prev;", + " if (newNode.prev !== null) {", + " newNode.prev.next = newNode;", + " }", + " newNode.next.prev = newNode;", + " if (node == this.head) {", + " this.head = newNode;", + " }", + " this.length++;", + " }", + " remove(data) {", + " if (this.head === null || this.tail === null) {", + " return;", + " }", + " let tempNode = this.head;", + " if (data === this.head.data) {", + " this.head = this.head.next;", + " }", + " if (data === this.tail.data) {", + " this.tail = this.tail.prev;", + " }", + " while (tempNode.next !== null && tempNode.data !== data) {", + " tempNode = tempNode.next;", + " }", + " if (tempNode.data === data) {", + " if (tempNode.prev !== null)", + " tempNode.prev.next = tempNode.next;", + " if (tempNode.next !== null)", + " tempNode.next.prev = tempNode.prev;", + " this.length--;", + " }", + " }", + " *values() {", + " let current = this.head;", + " while (current !== null) {", + " yield current.data;", + " current = current.next;", + " }", + " }", + "}", + "", + "class NodeParticle {", + " constructor() {", + " this.position = new THREE.Vector3();", + " this.velocity = new THREE.Vector3();", + " this.age = 0;", + " this.life = 1;", + " this.size = 1;", + " this.rotation = 0;", + " this.color = new THREE.Vector4(1, 1, 1, 1);", + " this.uvTile = 0;", + " }", + " get died() {", + " return this.age >= this.life;", + " }", + " reset() {", + " this.position.set(0, 0, 0);", + " this.velocity.set(0, 0, 0);", + " this.age = 0;", + " this.life = 1;", + " this.size = 1;", + " this.rotation = 0;", + " this.color.set(1, 1, 1, 1);", + " this.uvTile = 0;", + " }", + "}", + "class SpriteParticle {", + " constructor() {", + " this.startSpeed = 0;", + " this.startColor = new THREE.Vector4();", + " this.startSize = 1;", + " this.position = new THREE.Vector3();", + " this.velocity = new THREE.Vector3();", + " this.age = 0;", + " this.life = 1;", + " this.size = 1;", + " this.speedModifier = 1;", + " this.rotation = 0;", + " this.color = new THREE.Vector4();", + " this.uvTile = 0;", + " }", + " get died() {", + " return this.age >= this.life;", + " }", + "}", + "class RecordState {", + " constructor(position, size, color) {", + " this.position = position;", + " this.size = size;", + " this.color = color;", + " }", + "}", + "class TrailParticle {", + " constructor() {", + " this.startSpeed = 0;", + " this.startColor = new THREE.Vector4();", + " this.startSize = 1;", + " this.position = new THREE.Vector3();", + " this.velocity = new THREE.Vector3();", + " this.age = 0;", + " this.life = 1;", + " this.size = 1;", + " this.length = 100;", + " this.speedModifier = 1;", + " this.color = new THREE.Vector4();", + " this.previous = new LinkedList();", + " this.uvTile = 0;", + " }", + " update() {", + " if (this.age <= this.life) {", + " this.previous.push(new RecordState(this.position.clone(), this.size, this.color.clone()));", + " }", + " else {", + " if (this.previous.length > 0) {", + " this.previous.dequeue();", + " }", + " }", + " while (this.previous.length > this.length) {", + " this.previous.dequeue();", + " }", + " }", + " get died() {", + " return this.age >= this.life;", + " }", + " reset() {", + " this.previous.clear();", + " }", + "}", + "", + "class Bezier {", + " constructor(p1, p2, p3, p4) {", + " this.p = [p1, p2, p3, p4];", + " }", + " genValue(t) {", + " const t2 = t * t;", + " const t3 = t * t * t;", + " const mt = 1 - t;", + " const mt2 = mt * mt;", + " const mt3 = mt2 * mt;", + " return this.p[0] * mt3 + this.p[1] * mt2 * t * 3 + this.p[2] * mt * t2 * 3 + this.p[3] * t3;", + " }", + " derivativeCoefficients(points) {", + " const dpoints = [];", + " for (let p = points, c = p.length - 1; c > 0; c--) {", + " const list = [];", + " for (let j = 0; j < c; j++) {", + " const dpt = c * (p[j + 1] - p[j]);", + " list.push(dpt);", + " }", + " dpoints.push(list);", + " p = list;", + " }", + " return dpoints;", + " }", + " getSlope(t) {", + " const p = this.derivativeCoefficients(this.p)[0];", + " const mt = 1 - t;", + " const a = mt * mt;", + " const b = mt * t * 2;", + " const c = t * t;", + " return a * p[0] + b * p[1] + c * p[2];", + " }", + " controlCurve(d0, d1) {", + " this.p[1] = d0 / 3 + this.p[0];", + " this.p[2] = this.p[3] - d1 / 3;", + " }", + " hull(t) {", + " let p = this.p;", + " let _p = [], pt, idx = 0, i = 0, l = 0;", + " const q = [];", + " q[idx++] = p[0];", + " q[idx++] = p[1];", + " q[idx++] = p[2];", + " q[idx++] = p[3];", + " while (p.length > 1) {", + " _p = [];", + " for (i = 0, l = p.length - 1; i < l; i++) {", + " pt = t * p[i] + (1 - t) * p[i + 1];", + " q[idx++] = pt;", + " _p.push(pt);", + " }", + " p = _p;", + " }", + " return q;", + " }", + " split(t) {", + " const q = this.hull(t);", + " const result = {", + " left: new Bezier(q[0], q[4], q[7], q[9]),", + " right: new Bezier(q[9], q[8], q[6], q[3]),", + " span: q", + " };", + " return result;", + " }", + " clone() {", + " return new Bezier(this.p[0], this.p[1], this.p[2], this.p[3]);", + " }", + " toJSON() {", + " return {", + " p0: this.p[0],", + " p1: this.p[1],", + " p2: this.p[2],", + " p3: this.p[3],", + " };", + " }", + " static fromJSON(json) {", + " return new Bezier(json.p0, json.p1, json.p2, json.p3);", + " }", + "}", + "", + "const ColorToJSON = (color) => {", + " return { r: color.x, g: color.y, b: color.z, a: color.w };", + "};", + "const JSONToColor = (json) => {", + " return new THREE.Vector4(json.r, json.g, json.b, json.a);", + "};", + "const JSONToValue = (json, type) => {", + " switch (type) {", + " case 'Vector3':", + " return new THREE.Vector3(json.x, json.y, json.z);", + " case 'Vector4':", + " return new THREE.Vector4(json.x, json.y, json.z, json.w);", + " case 'Color':", + " return new THREE.Vector3(json.r, json.g, json.b);", + " case 'Number':", + " return json;", + " default:", + " return json;", + " }", + "};", + "const ValueToJSON = (value, type) => {", + " switch (type) {", + " case 'Vector3':", + " return { x: value.x, y: value.y, z: value.z };", + " case 'Vector4':", + " return { x: value.x, y: value.y, z: value.z, w: value.w };", + " case 'Color':", + " return { r: value.x, g: value.y, b: value.z };", + " case 'Number':", + " return value;", + " default:", + " return value;", + " }", + "};", + "", + "class RandomColor {", + " constructor(a, b) {", + " this.a = a;", + " this.b = b;", + " this.type = \"value\";", + " }", + " genColor(color) {", + " const rand = Math.random();", + " return color.copy(this.a).lerp(this.b, rand);", + " }", + " toJSON() {", + " return {", + " type: \"RandomColor\",", + " a: ColorToJSON(this.a),", + " b: ColorToJSON(this.b),", + " };", + " }", + " static fromJSON(json) {", + " return new RandomColor(JSONToColor(json.a), JSONToColor(json.b));", + " }", + " clone() {", + " return new RandomColor(this.a.clone(), this.b.clone());", + " }", + "}", + "", + "class ColorRange {", + " constructor(a, b) {", + " this.a = a;", + " this.b = b;", + " this.type = 'value';", + " }", + " genColor(color, t) {", + " return color.copy(this.a).lerp(this.b, Math.random());", + " }", + " toJSON() {", + " return {", + " type: 'ColorRange',", + " a: ColorToJSON(this.a),", + " b: ColorToJSON(this.b),", + " };", + " }", + " static fromJSON(json) {", + " return new ColorRange(JSONToColor(json.a), JSONToColor(json.b));", + " }", + " clone() {", + " return new ColorRange(this.a.clone(), this.b.clone());", + " }", + "}", + "", + "class ContinuousLinearFunction {", + " constructor(keys, subType) {", + " this.subType = subType;", + " this.type = 'function';", + " this.keys = keys;", + " }", + " findKey(t) {", + " let mid = 0;", + " let left = 0, right = this.keys.length - 1;", + " while (left + 1 < right) {", + " mid = Math.floor((left + right) / 2);", + " if (t < this.getStartX(mid))", + " right = mid - 1;", + " else if (t > this.getEndX(mid))", + " left = mid + 1;", + " else", + " return mid;", + " }", + " for (let i = left; i <= right; i++) {", + " if (t >= this.getStartX(i) && t <= this.getEndX(i))", + " return i;", + " }", + " return -1;", + " }", + " getStartX(index) {", + " return this.keys[index][1];", + " }", + " getEndX(index) {", + " if (index + 1 < this.keys.length)", + " return this.keys[index + 1][1];", + " return 1;", + " }", + " genValue(value, t) {", + " const index = this.findKey(t);", + " if (this.subType === 'Number') {", + " if (index === -1) {", + " return this.keys[0][0];", + " }", + " else if (index + 1 >= this.keys.length) {", + " return this.keys[this.keys.length - 1][0];", + " }", + " return ((this.keys[index + 1][0] - this.keys[index][0]) *", + " ((t - this.getStartX(index)) / (this.getEndX(index) - this.getStartX(index))) +", + " this.keys[index][0]);", + " }", + " else {", + " if (index === -1) {", + " return value.copy(this.keys[0][0]);", + " }", + " if (index + 1 >= this.keys.length) {", + " return value.copy(this.keys[this.keys.length - 1][0]);", + " }", + " return value", + " .copy(this.keys[index][0])", + " .lerp(this.keys[index + 1][0], (t - this.getStartX(index)) / (this.getEndX(index) - this.getStartX(index)));", + " }", + " }", + " toJSON() {", + " this.keys[0][0].constructor.name;", + " return {", + " type: 'CLinearFunction',", + " subType: this.subType,", + " keys: this.keys.map(([color, pos]) => ({ value: ValueToJSON(color, this.subType), pos: pos })),", + " };", + " }", + " static fromJSON(json) {", + " return new ContinuousLinearFunction(json.keys.map((pair) => [JSONToValue(pair.value, json.subType), pair.pos]), json.subType);", + " }", + " clone() {", + " if (this.subType === 'Number') {", + " return new ContinuousLinearFunction(this.keys.map(([value, pos]) => [value, pos]), this.subType);", + " }", + " else {", + " return new ContinuousLinearFunction(this.keys.map(([value, pos]) => [value.clone(), pos]), this.subType);", + " }", + " }", + "}", + "", + "const tempVec3 = new THREE.Vector3();", + "class Gradient {", + " constructor(color = [", + " [new THREE.Vector3(0, 0, 0), 0],", + " [new THREE.Vector3(1, 1, 1), 0],", + " ], alpha = [", + " [1, 0],", + " [1, 1],", + " ]) {", + " this.type = 'function';", + " this.color = new ContinuousLinearFunction(color, 'Color');", + " this.alpha = new ContinuousLinearFunction(alpha, 'Number');", + " }", + " genColor(color, t) {", + " this.color.genValue(tempVec3, t);", + " return color.set(tempVec3.x, tempVec3.y, tempVec3.z, this.alpha.genValue(1, t));", + " }", + " toJSON() {", + " return {", + " type: 'Gradient',", + " color: this.color.toJSON(),", + " alpha: this.alpha.toJSON(),", + " };", + " }", + " static fromJSON(json) {", + " if (json.functions) {", + " const keys = json.functions.map((func) => [ColorRange.fromJSON(func.function).a, func.start]);", + " if (json.functions.length > 0) {", + " keys.push([ColorRange.fromJSON(json.functions[json.functions.length - 1].function).b, 1]);", + " }", + " return new Gradient(keys.map((key) => [new THREE.Vector3(key[0].x, key[0].y, key[0].z), key[1]]), keys.map((key) => [key[0].w, key[1]]));", + " }", + " else {", + " const gradient = new Gradient();", + " gradient.alpha = ContinuousLinearFunction.fromJSON(json.alpha);", + " gradient.color = ContinuousLinearFunction.fromJSON(json.color);", + " return gradient;", + " }", + " }", + " clone() {", + " const gradient = new Gradient();", + " gradient.alpha = this.alpha.clone();", + " gradient.color = this.color.clone();", + " return gradient;", + " }", + "}", + "", + "const tempColor = new THREE.Vector4();", + "class RandomColorBetweenGradient {", + " constructor(gradient1, gradient2) {", + " this.type = 'memorizedFunction';", + " this.gradient1 = gradient1;", + " this.gradient2 = gradient2;", + " }", + " startGen(memory) {", + " memory.rand = Math.random();", + " }", + " genColor(color, t, memory) {", + " this.gradient1.genColor(color, t);", + " this.gradient2.genColor(tempColor, t);", + " if (memory && memory.rand) {", + " color.lerp(tempColor, memory.rand);", + " }", + " else {", + " color.lerp(tempColor, Math.random());", + " }", + " return color;", + " }", + " toJSON() {", + " return {", + " type: 'RandomColorBetweenGradient',", + " gradient1: this.gradient1.toJSON(),", + " gradient2: this.gradient2.toJSON(),", + " };", + " }", + " static fromJSON(json) {", + " return new RandomColorBetweenGradient(Gradient.fromJSON(json.gradient1), Gradient.fromJSON(json.gradient2));", + " }", + " clone() {", + " return new RandomColorBetweenGradient(this.gradient1.clone(), this.gradient2.clone());", + " }", + "}", + "", + "class ConstantColor {", + " constructor(color) {", + " this.color = color;", + " this.type = 'value';", + " }", + " genColor(color) {", + " return color.copy(this.color);", + " }", + " toJSON() {", + " return {", + " type: 'ConstantColor',", + " color: ColorToJSON(this.color),", + " };", + " }", + " static fromJSON(json) {", + " return new ConstantColor(JSONToColor(json.color));", + " }", + " clone() {", + " return new ConstantColor(this.color.clone());", + " }", + "}", + "function ColorGeneratorFromJSON(json) {", + " switch (json.type) {", + " case 'ConstantColor':", + " return ConstantColor.fromJSON(json);", + " case 'ColorRange':", + " return ColorRange.fromJSON(json);", + " case 'RandomColor':", + " return RandomColor.fromJSON(json);", + " case 'Gradient':", + " return Gradient.fromJSON(json);", + " case 'RandomColorBetweenGradient':", + " return RandomColorBetweenGradient.fromJSON(json);", + " default:", + " return new ConstantColor(new THREE.Vector4(1, 1, 1, 1));", + " }", + "}", + "", + "class ConstantValue {", + " constructor(value) {", + " this.value = value;", + " this.type = 'value';", + " }", + " genValue() {", + " return this.value;", + " }", + " toJSON() {", + " return {", + " type: \"ConstantValue\",", + " value: this.value", + " };", + " }", + " static fromJSON(json) {", + " return new ConstantValue(json.value);", + " }", + " clone() {", + " return new ConstantValue(this.value);", + " }", + "}", + "", + "class IntervalValue {", + " constructor(a, b) {", + " this.a = a;", + " this.b = b;", + " this.type = 'value';", + " }", + " genValue() {", + " return THREE.MathUtils.lerp(this.a, this.b, Math.random());", + " }", + " toJSON() {", + " return {", + " type: 'IntervalValue',", + " a: this.a,", + " b: this.b,", + " };", + " }", + " static fromJSON(json) {", + " return new IntervalValue(json.a, json.b);", + " }", + " clone() {", + " return new IntervalValue(this.a, this.b);", + " }", + "}", + "", + "class PiecewiseFunction {", + " constructor() {", + " this.functions = new Array();", + " }", + " findFunction(t) {", + " let mid = 0;", + " let left = 0, right = this.functions.length - 1;", + " while (left + 1 < right) {", + " mid = Math.floor((left + right) / 2);", + " if (t < this.getStartX(mid))", + " right = mid - 1;", + " else if (t > this.getEndX(mid))", + " left = mid + 1;", + " else", + " return mid;", + " }", + " for (let i = left; i <= right; i++) {", + " if (t >= this.functions[i][1] && t <= this.getEndX(i))", + " return i;", + " }", + " return -1;", + " }", + " getStartX(index) {", + " return this.functions[index][1];", + " }", + " setStartX(index, x) {", + " if (index > 0)", + " this.functions[index][1] = x;", + " }", + " getEndX(index) {", + " if (index + 1 < this.functions.length)", + " return this.functions[index + 1][1];", + " return 1;", + " }", + " setEndX(index, x) {", + " if (index + 1 < this.functions.length)", + " this.functions[index + 1][1] = x;", + " }", + " insertFunction(t, func) {", + " const index = this.findFunction(t);", + " this.functions.splice(index + 1, 0, [func, t]);", + " }", + " removeFunction(index) {", + " return this.functions.splice(index, 1)[0][0];", + " }", + " getFunction(index) {", + " return this.functions[index][0];", + " }", + " setFunction(index, func) {", + " this.functions[index][0] = func;", + " }", + " get numOfFunctions() {", + " return this.functions.length;", + " }", + "}", + "", + "class PiecewiseBezier extends PiecewiseFunction {", + " constructor(curves = [[new Bezier(0, 1.0 / 3, 1.0 / 3 * 2, 1), 0]]) {", + " super();", + " this.type = \"function\";", + " this.functions = curves;", + " }", + " genValue(t = 0) {", + " const index = this.findFunction(t);", + " if (index === -1) {", + " return 0;", + " }", + " return this.functions[index][0].genValue((t - this.getStartX(index)) / (this.getEndX(index) - this.getStartX(index)));", + " }", + " toSVG(length, segments) {", + " if (segments < 1)", + " return \"\";", + " let result = [\"M\", 0, this.functions[0][0].p[0]].join(\" \");", + " for (let i = 1.0 / segments; i <= 1; i += 1.0 / segments) {", + " result = [result, \"L\", i * length, this.genValue(i)].join(\" \");", + " }", + " return result;", + " }", + " toJSON() {", + " return {", + " type: \"PiecewiseBezier\",", + " functions: this.functions.map(([bezier, start]) => ({ function: bezier.toJSON(), start: start })),", + " };", + " }", + " static fromJSON(json) {", + " return new PiecewiseBezier(json.functions.map((piecewiseFunction) => ([Bezier.fromJSON(piecewiseFunction.function), piecewiseFunction.start])));", + " }", + " clone() {", + " return new PiecewiseBezier(this.functions.map(([bezier, start]) => ([bezier.clone(), start])));", + " }", + "}", + "", + "function ValueGeneratorFromJSON(json) {", + " switch (json.type) {", + " case 'ConstantValue':", + " return ConstantValue.fromJSON(json);", + " case 'IntervalValue':", + " return IntervalValue.fromJSON(json);", + " case 'PiecewiseBezier':", + " return PiecewiseBezier.fromJSON(json);", + " default:", + " return new ConstantValue(0);", + " }", + "}", + "", + "class RandomQuatGenerator {", + " constructor() {", + " this.type = \"rotation\";", + " }", + " genValue(quat, t) {", + " let x, y, z, u, v, w;", + " do {", + " x = Math.random() * 2 - 1;", + " y = Math.random() * 2 - 1;", + " z = x * x + y * y;", + " } while (z > 1);", + " do {", + " u = Math.random() * 2 - 1;", + " v = Math.random() * 2 - 1;", + " w = u * u + v * v;", + " } while (w > 1);", + " const s = Math.sqrt((1 - z) / w);", + " quat.set(x, y, s * u, s * v);", + " return quat;", + " }", + " toJSON() {", + " return {", + " type: \"RandomQuat\"", + " };", + " }", + " static fromJSON(json) {", + " return new RandomQuatGenerator();", + " }", + " clone() {", + " return new RandomQuatGenerator();", + " }", + "}", + "", + "class AxisAngleGenerator {", + " constructor(axis, angle) {", + " this.axis = axis;", + " this.angle = angle;", + " this.type = 'rotation';", + " }", + " genValue(quat, t) {", + " return quat.setFromAxisAngle(this.axis, this.angle.genValue(t));", + " }", + " toJSON() {", + " return {", + " type: 'AxisAngle',", + " axis: { x: this.axis.x, y: this.axis.y, z: this.axis.z },", + " angle: this.angle.toJSON(),", + " };", + " }", + " static fromJSON(json) {", + " return new AxisAngleGenerator(new THREE.Vector3(json.axis.x, json.axis.y, json.axis.z), ValueGeneratorFromJSON(json.angle));", + " }", + " clone() {", + " return new AxisAngleGenerator(this.axis.clone(), this.angle.clone());", + " }", + "}", + "", + "class EulerGenerator {", + " constructor(angleX, angleY, angleZ, eulerOrder) {", + " this.angleX = angleX;", + " this.angleY = angleY;", + " this.angleZ = angleZ;", + " this.type = 'rotation';", + " this.eular = new THREE.Euler(0, 0, 0, eulerOrder);", + " }", + " genValue(quat, t) {", + " this.eular.set(this.angleX.genValue(t), this.angleY.genValue(t), this.angleZ.genValue(t));", + " return quat.setFromEuler(this.eular);", + " }", + " toJSON() {", + " return {", + " type: 'Euler',", + " angleX: this.angleX.toJSON(),", + " angleY: this.angleY.toJSON(),", + " angleZ: this.angleZ.toJSON(),", + " eulerOrder: this.eular.order,", + " };", + " }", + " static fromJSON(json) {", + " return new EulerGenerator(ValueGeneratorFromJSON(json.angleX), ValueGeneratorFromJSON(json.angleY), ValueGeneratorFromJSON(json.angleZ), json.eulerOrder);", + " }", + " clone() {", + " return new EulerGenerator(this.angleX, this.angleY, this.angleZ, this.eular.order);", + " }", + "}", + "", + "function RotationGeneratorFromJSON(json) {", + " switch (json.type) {", + " case 'AxisAngle':", + " return AxisAngleGenerator.fromJSON(json);", + " case 'Euler':", + " return EulerGenerator.fromJSON(json);", + " case 'RandomQuat':", + " return RandomQuatGenerator.fromJSON(json);", + " default:", + " return new RandomQuatGenerator();", + " }", + "}", + "", + "function GeneratorFromJSON(json) {", + " switch (json.type) {", + " case 'ConstantValue':", + " case 'IntervalValue':", + " case 'PiecewiseBezier':", + " return ValueGeneratorFromJSON(json);", + " case 'AxisAngle':", + " case 'RandomQuat':", + " case 'Euler':", + " return RotationGeneratorFromJSON(json);", + " default:", + " return new ConstantValue(0);", + " }", + "}", + "", + "class ColorOverLife {", + " constructor(color) {", + " this.color = color;", + " this.type = 'ColorOverLife';", + " }", + " initialize(particle) {", + " if (this.color.type === 'memorizedFunction') {", + " particle.colorOverLifeMemory = {};", + " this.color.startGen(particle.colorOverLifeMemory);", + " }", + " }", + " update(particle, delta) {", + " if (this.color.type === 'memorizedFunction') {", + " this.color.genColor(particle.color, particle.age / particle.life, particle.colorOverLifeMemory);", + " }", + " else {", + " this.color.genColor(particle.color, particle.age / particle.life);", + " }", + " particle.color.x *= particle.startColor.x;", + " particle.color.y *= particle.startColor.y;", + " particle.color.z *= particle.startColor.z;", + " particle.color.w *= particle.startColor.w;", + " }", + " frameUpdate(delta) { }", + " toJSON() {", + " return {", + " type: this.type,", + " color: this.color.toJSON(),", + " };", + " }", + " static fromJSON(json) {", + " return new ColorOverLife(ColorGeneratorFromJSON(json.color));", + " }", + " clone() {", + " return new ColorOverLife(this.color.clone());", + " }", + " reset() { }", + "}", + "", + "class RotationOverLife {", + " constructor(angularVelocity) {", + " this.angularVelocity = angularVelocity;", + " this.type = 'RotationOverLife';", + " this.dynamic = !(angularVelocity instanceof ConstantValue || angularVelocity instanceof IntervalValue);", + " }", + " initialize(particle) {", + " this.dynamic = !(this.angularVelocity instanceof ConstantValue || this.angularVelocity instanceof IntervalValue);", + " if (!this.dynamic && typeof particle.rotation === 'number') {", + " particle.angularVelocity = this.angularVelocity.genValue();", + " }", + " }", + " update(particle, delta) {", + " if (!this.dynamic) {", + " if (typeof particle.rotation === 'number') {", + " particle.rotation += delta * particle.angularVelocity;", + " }", + " }", + " else {", + " if (typeof particle.rotation === 'number') {", + " particle.rotation +=", + " delta * this.angularVelocity.genValue(particle.age / particle.life);", + " }", + " }", + " }", + " toJSON() {", + " return {", + " type: this.type,", + " angularVelocity: this.angularVelocity.toJSON(),", + " };", + " }", + " static fromJSON(json) {", + " return new RotationOverLife(ValueGeneratorFromJSON(json.angularVelocity));", + " }", + " frameUpdate(delta) { }", + " clone() {", + " return new RotationOverLife(this.angularVelocity.clone());", + " }", + " reset() { }", + "}", + "", + "const IdentityQuaternion = new THREE.Quaternion();", + "class Rotation3DOverLife {", + " constructor(angularVelocity) {", + " this.angularVelocity = angularVelocity;", + " this.type = 'Rotation3DOverLife';", + " this.tempQuat = new THREE.Quaternion();", + " this.dynamic = !(angularVelocity instanceof RandomQuatGenerator);", + " }", + " initialize(particle) {", + " this.dynamic = !(this.angularVelocity instanceof RandomQuatGenerator);", + " if (particle.rotation instanceof THREE.Quaternion) {", + " particle.angularVelocity = new THREE.Quaternion();", + " this.angularVelocity.genValue(particle.angularVelocity);", + " }", + " }", + " update(particle, delta) {", + " if (particle.rotation instanceof THREE.Quaternion) {", + " if (!this.dynamic) {", + " this.tempQuat.slerpQuaternions(IdentityQuaternion, particle.angularVelocity, delta);", + " particle.rotation.multiply(this.tempQuat);", + " }", + " else {", + " this.angularVelocity.genValue(this.tempQuat, particle.age / particle.life);", + " this.tempQuat.slerpQuaternions(IdentityQuaternion, this.tempQuat, delta);", + " particle.rotation.multiply(this.tempQuat);", + " }", + " }", + " }", + " toJSON() {", + " return {", + " type: this.type,", + " angularVelocity: this.angularVelocity.toJSON(),", + " };", + " }", + " static fromJSON(json) {", + " return new Rotation3DOverLife(RotationGeneratorFromJSON(json.angularVelocity));", + " }", + " frameUpdate(delta) { }", + " clone() {", + " return new Rotation3DOverLife(this.angularVelocity.clone());", + " }", + " reset() { }", + "}", + "", + "class ForceOverLife {", + " initialize(particle) { }", + " constructor(x, y, z) {", + " this.x = x;", + " this.y = y;", + " this.z = z;", + " this.type = 'ForceOverLife';", + " this._temp = new THREE.Vector3();", + " }", + " update(particle, delta) {", + " this._temp.set(this.x.genValue(particle.age / particle.life), this.y.genValue(particle.age / particle.life), this.z.genValue(particle.age / particle.life));", + " particle.velocity.addScaledVector(this._temp, delta);", + " }", + " toJSON() {", + " return {", + " type: this.type,", + " x: this.x.toJSON(),", + " y: this.y.toJSON(),", + " z: this.z.toJSON(),", + " };", + " }", + " static fromJSON(json) {", + " return new ForceOverLife(ValueGeneratorFromJSON(json.x), ValueGeneratorFromJSON(json.y), ValueGeneratorFromJSON(json.z));", + " }", + " frameUpdate(delta) { }", + " clone() {", + " return new ForceOverLife(this.x.clone(), this.y.clone(), this.z.clone());", + " }", + " reset() { }", + "}", + "", + "class SizeOverLife {", + " initialize(particle) {", + " }", + " constructor(size) {", + " this.size = size;", + " this.type = 'SizeOverLife';", + " }", + " update(particle) {", + " particle.size = particle.startSize * this.size.genValue(particle.age / particle.life);", + " }", + " toJSON() {", + " return {", + " type: this.type,", + " size: this.size.toJSON(),", + " };", + " }", + " static fromJSON(json) {", + " return new SizeOverLife(ValueGeneratorFromJSON(json.size));", + " }", + " frameUpdate(delta) {", + " }", + " clone() {", + " return new SizeOverLife(this.size.clone());", + " }", + " reset() {", + " }", + "}", + "", + "class SpeedOverLife {", + " initialize(particle) {", + " }", + " constructor(speed) {", + " this.speed = speed;", + " this.type = 'SpeedOverLife';", + " }", + " update(particle) {", + " particle.speedModifier = this.speed.genValue(particle.age / particle.life);", + " }", + " toJSON() {", + " return {", + " type: this.type,", + " speed: this.speed.toJSON(),", + " };", + " }", + " static fromJSON(json) {", + " return new SpeedOverLife(ValueGeneratorFromJSON(json.speed));", + " }", + " frameUpdate(delta) {", + " }", + " clone() {", + " return new SpeedOverLife(this.speed.clone());", + " }", + " reset() {", + " }", + "}", + "", + "class FrameOverLife {", + " constructor(frame) {", + " this.frame = frame;", + " this.type = 'FrameOverLife';", + " }", + " initialize(particle) {", + " if (!(this.frame instanceof PiecewiseBezier)) {", + " particle.uvTile = Math.floor(this.frame.genValue(0) + 0.001);", + " }", + " }", + " update(particle, delta) {", + " if (this.frame instanceof PiecewiseBezier) {", + " particle.uvTile = Math.floor(this.frame.genValue(particle.age / particle.life) + 0.001);", + " }", + " }", + " frameUpdate(delta) { }", + " toJSON() {", + " return {", + " type: this.type,", + " frame: this.frame.toJSON(),", + " };", + " }", + " static fromJSON(json) {", + " return new FrameOverLife(ValueGeneratorFromJSON(json.frame));", + " }", + " clone() {", + " return new FrameOverLife(this.frame.clone());", + " }", + " reset() { }", + "}", + "", + "new THREE.Vector3(0, 0, 1);", + "class OrbitOverLife {", + " constructor(orbitSpeed, axis = new THREE.Vector3(0, 1, 0)) {", + " this.orbitSpeed = orbitSpeed;", + " this.axis = axis;", + " this.type = 'OrbitOverLife';", + " this.temp = new THREE.Vector3();", + " this.rotation = new THREE.Quaternion();", + " this.line = new THREE.Line3();", + " }", + " initialize(particle) {", + " }", + " update(particle, delta) {", + " this.line.set(new THREE.Vector3(0, 0, 0), this.axis);", + " this.line.closestPointToPoint(particle.position, false, this.temp);", + " this.rotation.setFromAxisAngle(this.axis, this.orbitSpeed.genValue(particle.age / particle.life) * delta);", + " particle.position.sub(this.temp);", + " particle.position.applyQuaternion(this.rotation);", + " particle.position.add(this.temp);", + " }", + " frameUpdate(delta) {", + " }", + " toJSON() {", + " return {", + " type: this.type,", + " orbitSpeed: this.orbitSpeed.toJSON(),", + " axis: [this.axis.x, this.axis.y, this.axis.z],", + " };", + " }", + " static fromJSON(json) {", + " return new OrbitOverLife(ValueGeneratorFromJSON(json.orbitSpeed), json.axis ? new THREE.Vector3(json.axis[0], json.axis[1], json.axis[2]) : undefined);", + " }", + " clone() {", + " return new OrbitOverLife(this.orbitSpeed.clone());", + " }", + " reset() {", + " }", + "}", + "", + "class WidthOverLength {", + " initialize(particle) {", + " }", + " constructor(width) {", + " this.width = width;", + " this.type = 'WidthOverLength';", + " }", + " update(particle) {", + " if (particle instanceof TrailParticle) {", + " const iter = particle.previous.values();", + " for (let i = 0; i < particle.previous.length; i++) {", + " const cur = iter.next();", + " // We added a multiplication with size because it's more convenient for the extension.", + " cur.value.size = this.width.genValue((particle.previous.length - i) / particle.length) * particle.size;", + " }", + " }", + " }", + " frameUpdate(delta) {", + " }", + " toJSON() {", + " return {", + " type: this.type,", + " width: this.width.toJSON(),", + " };", + " }", + " static fromJSON(json) {", + " return new WidthOverLength(ValueGeneratorFromJSON(json.width));", + " }", + " clone() {", + " return new WidthOverLength(this.width.clone());", + " }", + " reset() {", + " }", + "}", + "", + "class ApplyForce {", + " constructor(direction, magnitude) {", + " this.direction = direction;", + " this.magnitude = magnitude;", + " this.type = 'ApplyForce';", + " this.magnitudeValue = this.magnitude.genValue();", + " }", + " initialize(particle) {", + " }", + " update(particle, delta) {", + " particle.velocity.addScaledVector(this.direction, this.magnitudeValue * delta);", + " }", + " frameUpdate(delta) {", + " this.magnitudeValue = this.magnitude.genValue();", + " }", + " toJSON() {", + " return {", + " type: this.type,", + " direction: [this.direction.x, this.direction.y, this.direction.z],", + " magnitude: this.magnitude.toJSON(),", + " };", + " }", + " static fromJSON(json) {", + " var _a;", + " return new ApplyForce(new THREE.Vector3(json.direction[0], json.direction[1], json.direction[2]), ValueGeneratorFromJSON((_a = json.magnitude) !== null && _a !== void 0 ? _a : json.force));", + " }", + " clone() {", + " return new ApplyForce(this.direction.clone(), this.magnitude.clone());", + " }", + " reset() {", + " }", + "}", + "", + "class GravityForce {", + " constructor(center, magnitude) {", + " this.center = center;", + " this.magnitude = magnitude;", + " this.type = 'GravityForce';", + " this.temp = new THREE.Vector3();", + " }", + " initialize(particle) {", + " }", + " update(particle, delta) {", + " this.temp.copy(this.center).sub(particle.position).normalize();", + " particle.velocity.addScaledVector(this.temp, this.magnitude / particle.position.distanceToSquared(this.center) * delta);", + " }", + " frameUpdate(delta) {", + " }", + " toJSON() {", + " return {", + " type: this.type,", + " center: [this.center.x, this.center.y, this.center.z],", + " magnitude: this.magnitude,", + " };", + " }", + " static fromJSON(json) {", + " return new GravityForce(new THREE.Vector3(json.center[0], json.center[1], json.center[2]), json.magnitude);", + " }", + " clone() {", + " return new GravityForce(this.center.clone(), this.magnitude);", + " }", + " reset() {", + " }", + "}", + "", + "new THREE.Vector3(0, 0, 1);", + "class ChangeEmitDirection {", + " constructor(angle) {", + " this.angle = angle;", + " this.type = 'ChangeEmitDirection';", + " this._temp = new THREE.Vector3();", + " this._q = new THREE.Quaternion();", + " }", + " initialize(particle) {", + " const len = particle.velocity.length();", + " if (len == 0)", + " return;", + " particle.velocity.normalize();", + " if (particle.velocity.x === 0 && particle.velocity.y === 0) {", + " this._temp.set(0, particle.velocity.z, 0);", + " }", + " else {", + " this._temp.set(-particle.velocity.y, particle.velocity.x, 0);", + " }", + " this._q.setFromAxisAngle(this._temp.normalize(), this.angle.genValue());", + " this._temp.copy(particle.velocity);", + " particle.velocity.applyQuaternion(this._q);", + " this._q.setFromAxisAngle(this._temp, Math.random() * Math.PI * 2);", + " particle.velocity.applyQuaternion(this._q);", + " particle.velocity.setLength(len);", + " }", + " update(particle, delta) {", + " }", + " frameUpdate(delta) {", + " }", + " toJSON() {", + " return {", + " type: this.type,", + " angle: this.angle.toJSON(),", + " };", + " }", + " static fromJSON(json) {", + " return new ChangeEmitDirection(ValueGeneratorFromJSON(json.angle));", + " }", + " clone() {", + " return new ChangeEmitDirection(this.angle);", + " }", + " reset() {", + " }", + "}", + "", + "const VECTOR_ONE = new THREE.Vector3(1, 1, 1);", + "const VECTOR_Z = new THREE.Vector3(0, 0, 1);", + "let SubParticleEmitMode = void 0;", + "(function (SubParticleEmitMode) {", + " SubParticleEmitMode[SubParticleEmitMode[\"Death\"] = 0] = \"Death\";", + " SubParticleEmitMode[SubParticleEmitMode[\"Birth\"] = 1] = \"Birth\";", + " SubParticleEmitMode[SubParticleEmitMode[\"Frame\"] = 2] = \"Frame\";", + "})(SubParticleEmitMode || (SubParticleEmitMode = {}));", + "class EmitSubParticleSystem {", + " constructor(particleSystem, useVelocityAsBasis, subParticleSystem, mode = SubParticleEmitMode.Frame, emitProbability = 1) {", + " this.particleSystem = particleSystem;", + " this.useVelocityAsBasis = useVelocityAsBasis;", + " this.subParticleSystem = subParticleSystem;", + " this.mode = mode;", + " this.emitProbability = emitProbability;", + " this.type = 'EmitSubParticleSystem';", + " this.q_ = new THREE.Quaternion();", + " this.v_ = new THREE.Vector3();", + " this.v2_ = new THREE.Vector3();", + " this.subEmissions = new Array();", + " if (this.subParticleSystem && this.subParticleSystem.system) {", + " this.subParticleSystem.system.onlyUsedByOther = true;", + " }", + " }", + " initialize(particle) {", + " }", + " update(particle, delta) {", + " if (this.mode === SubParticleEmitMode.Frame) {", + " this.emit(particle, delta);", + " }", + " else if (this.mode === SubParticleEmitMode.Birth && particle.age === 0) {", + " this.emit(particle, delta);", + " }", + " else if (this.mode === SubParticleEmitMode.Death && particle.age + delta >= particle.life) {", + " this.emit(particle, delta);", + " }", + " }", + " emit(particle, delta) {", + " if (!this.subParticleSystem)", + " return;", + " if (Math.random() > this.emitProbability) {", + " return;", + " }", + " const m = new THREE.Matrix4();", + " this.setMatrixFromParticle(m, particle);", + " this.subEmissions.push({", + " burstIndex: 0,", + " burstWaveIndex: 0,", + " time: 0,", + " waitEmiting: 0,", + " matrix: m,", + " travelDistance: 0,", + " particle: particle,", + " });", + " }", + " frameUpdate(delta) {", + " if (!this.subParticleSystem)", + " return;", + " for (let i = 0; i < this.subEmissions.length; i++) {", + " if (this.subEmissions[i].time >= this.subParticleSystem.system.duration) {", + " this.subEmissions[i] = this.subEmissions[this.subEmissions.length - 1];", + " this.subEmissions.length = this.subEmissions.length - 1;", + " i--;", + " }", + " else {", + " let subEmissionState = this.subEmissions[i];", + " if (subEmissionState.particle && subEmissionState.particle.age < subEmissionState.particle.life) {", + " this.setMatrixFromParticle(subEmissionState.matrix, subEmissionState.particle);", + " }", + " else {", + " subEmissionState.particle = undefined;", + " }", + " this.subParticleSystem.system.emit(delta, subEmissionState, subEmissionState.matrix);", + " }", + " }", + " }", + " toJSON() {", + " return {", + " type: this.type,", + " subParticleSystem: this.subParticleSystem ? this.subParticleSystem.uuid : '',", + " useVelocityAsBasis: this.useVelocityAsBasis,", + " mode: this.mode,", + " emitProbability: this.emitProbability,", + " };", + " }", + " static fromJSON(json, particleSystem) {", + " return new EmitSubParticleSystem(particleSystem, json.useVelocityAsBasis, json.subParticleSystem, json.mode, json.emitProbability);", + " }", + " clone() {", + " return new EmitSubParticleSystem(this.particleSystem, this.useVelocityAsBasis, this.subParticleSystem, this.mode, this.emitProbability);", + " }", + " reset() { }", + " setMatrixFromParticle(m, particle) {", + " let rotation;", + " if (particle.rotation === undefined || this.useVelocityAsBasis) {", + " if (particle.velocity.x === 0 &&", + " particle.velocity.y === 0 &&", + " (particle.velocity.z === 1 || particle.velocity.z === 0)) {", + " m.set(1, 0, 0, particle.position.x, 0, 1, 0, particle.position.y, 0, 0, 1, particle.position.z, 0, 0, 0, 1);", + " }", + " else {", + " this.v_.copy(VECTOR_Z).cross(particle.velocity);", + " this.v2_.copy(particle.velocity).cross(this.v_);", + " const len = this.v_.length();", + " const len2 = this.v2_.length();", + " m.set(this.v_.x / len, this.v2_.x / len2, particle.velocity.x, particle.position.x, this.v_.y / len, this.v2_.y / len2, particle.velocity.y, particle.position.y, this.v_.z / len, this.v2_.z / len2, particle.velocity.z, particle.position.z, 0, 0, 0, 1);", + " }", + " }", + " else {", + " if (particle.rotation instanceof THREE.Quaternion) {", + " rotation = particle.rotation;", + " }", + " else {", + " this.q_.setFromAxisAngle(VECTOR_Z, particle.rotation);", + " rotation = this.q_;", + " }", + " m.compose(particle.position, rotation, VECTOR_ONE);", + " }", + " if (!this.particleSystem.worldSpace) {", + " m.multiplyMatrices(this.particleSystem.emitter.matrixWorld, m);", + " }", + " }", + "}", + "", + "const F2 = 0.5 * (Math.sqrt(3.0) - 1.0);", + "const G2 = (3.0 - Math.sqrt(3.0)) / 6.0;", + "const F3 = 1.0 / 3.0;", + "const G3 = 1.0 / 6.0;", + "const F4 = (Math.sqrt(5.0) - 1.0) / 4.0;", + "const G4 = (5.0 - Math.sqrt(5.0)) / 20.0;", + "const grad3 = new Float32Array([1, 1, 0,", + " -1, 1, 0,", + " 1, -1, 0,", + " -1, -1, 0,", + " 1, 0, 1,", + " -1, 0, 1,", + " 1, 0, -1,", + " -1, 0, -1,", + " 0, 1, 1,", + " 0, -1, 1,", + " 0, 1, -1,", + " 0, -1, -1]);", + "const grad4 = new Float32Array([0, 1, 1, 1, 0, 1, 1, -1, 0, 1, -1, 1, 0, 1, -1, -1,", + " 0, -1, 1, 1, 0, -1, 1, -1, 0, -1, -1, 1, 0, -1, -1, -1,", + " 1, 0, 1, 1, 1, 0, 1, -1, 1, 0, -1, 1, 1, 0, -1, -1,", + " -1, 0, 1, 1, -1, 0, 1, -1, -1, 0, -1, 1, -1, 0, -1, -1,", + " 1, 1, 0, 1, 1, 1, 0, -1, 1, -1, 0, 1, 1, -1, 0, -1,", + " -1, 1, 0, 1, -1, 1, 0, -1, -1, -1, 0, 1, -1, -1, 0, -1,", + " 1, 1, 1, 0, 1, 1, -1, 0, 1, -1, 1, 0, 1, -1, -1, 0,", + " -1, 1, 1, 0, -1, 1, -1, 0, -1, -1, 1, 0, -1, -1, -1, 0]);", + "class SimplexNoise {", + " constructor(randomOrSeed = Math.random) {", + " const random = typeof randomOrSeed == 'function' ? randomOrSeed : alea(randomOrSeed);", + " this.p = buildPermutationTable(random);", + " this.perm = new Uint8Array(512);", + " this.permMod12 = new Uint8Array(512);", + " for (let i = 0; i < 512; i++) {", + " this.perm[i] = this.p[i & 255];", + " this.permMod12[i] = this.perm[i] % 12;", + " }", + " }", + " noise2D(x, y) {", + " const permMod12 = this.permMod12;", + " const perm = this.perm;", + " let n0 = 0;", + " let n1 = 0;", + " let n2 = 0;", + " const s = (x + y) * F2;", + " const i = Math.floor(x + s);", + " const j = Math.floor(y + s);", + " const t = (i + j) * G2;", + " const X0 = i - t;", + " const Y0 = j - t;", + " const x0 = x - X0;", + " const y0 = y - Y0;", + " let i1, j1;", + " if (x0 > y0) {", + " i1 = 1;", + " j1 = 0;", + " }", + " else {", + " i1 = 0;", + " j1 = 1;", + " }", + " const x1 = x0 - i1 + G2;", + " const y1 = y0 - j1 + G2;", + " const x2 = x0 - 1.0 + 2.0 * G2;", + " const y2 = y0 - 1.0 + 2.0 * G2;", + " const ii = i & 255;", + " const jj = j & 255;", + " let t0 = 0.5 - x0 * x0 - y0 * y0;", + " if (t0 >= 0) {", + " const gi0 = permMod12[ii + perm[jj]] * 3;", + " t0 *= t0;", + " n0 = t0 * t0 * (grad3[gi0] * x0 + grad3[gi0 + 1] * y0);", + " }", + " let t1 = 0.5 - x1 * x1 - y1 * y1;", + " if (t1 >= 0) {", + " const gi1 = permMod12[ii + i1 + perm[jj + j1]] * 3;", + " t1 *= t1;", + " n1 = t1 * t1 * (grad3[gi1] * x1 + grad3[gi1 + 1] * y1);", + " }", + " let t2 = 0.5 - x2 * x2 - y2 * y2;", + " if (t2 >= 0) {", + " const gi2 = permMod12[ii + 1 + perm[jj + 1]] * 3;", + " t2 *= t2;", + " n2 = t2 * t2 * (grad3[gi2] * x2 + grad3[gi2 + 1] * y2);", + " }", + " return 70.0 * (n0 + n1 + n2);", + " }", + " noise3D(x, y, z) {", + " const permMod12 = this.permMod12;", + " const perm = this.perm;", + " let n0, n1, n2, n3;", + " const s = (x + y + z) * F3;", + " const i = Math.floor(x + s);", + " const j = Math.floor(y + s);", + " const k = Math.floor(z + s);", + " const t = (i + j + k) * G3;", + " const X0 = i - t;", + " const Y0 = j - t;", + " const Z0 = k - t;", + " const x0 = x - X0;", + " const y0 = y - Y0;", + " const z0 = z - Z0;", + " let i1, j1, k1;", + " let i2, j2, k2;", + " if (x0 >= y0) {", + " if (y0 >= z0) {", + " i1 = 1;", + " j1 = 0;", + " k1 = 0;", + " i2 = 1;", + " j2 = 1;", + " k2 = 0;", + " }", + " else if (x0 >= z0) {", + " i1 = 1;", + " j1 = 0;", + " k1 = 0;", + " i2 = 1;", + " j2 = 0;", + " k2 = 1;", + " }", + " else {", + " i1 = 0;", + " j1 = 0;", + " k1 = 1;", + " i2 = 1;", + " j2 = 0;", + " k2 = 1;", + " }", + " }", + " else {", + " if (y0 < z0) {", + " i1 = 0;", + " j1 = 0;", + " k1 = 1;", + " i2 = 0;", + " j2 = 1;", + " k2 = 1;", + " }", + " else if (x0 < z0) {", + " i1 = 0;", + " j1 = 1;", + " k1 = 0;", + " i2 = 0;", + " j2 = 1;", + " k2 = 1;", + " }", + " else {", + " i1 = 0;", + " j1 = 1;", + " k1 = 0;", + " i2 = 1;", + " j2 = 1;", + " k2 = 0;", + " }", + " }", + " const x1 = x0 - i1 + G3;", + " const y1 = y0 - j1 + G3;", + " const z1 = z0 - k1 + G3;", + " const x2 = x0 - i2 + 2.0 * G3;", + " const y2 = y0 - j2 + 2.0 * G3;", + " const z2 = z0 - k2 + 2.0 * G3;", + " const x3 = x0 - 1.0 + 3.0 * G3;", + " const y3 = y0 - 1.0 + 3.0 * G3;", + " const z3 = z0 - 1.0 + 3.0 * G3;", + " const ii = i & 255;", + " const jj = j & 255;", + " const kk = k & 255;", + " let t0 = 0.6 - x0 * x0 - y0 * y0 - z0 * z0;", + " if (t0 < 0)", + " n0 = 0.0;", + " else {", + " const gi0 = permMod12[ii + perm[jj + perm[kk]]] * 3;", + " t0 *= t0;", + " n0 = t0 * t0 * (grad3[gi0] * x0 + grad3[gi0 + 1] * y0 + grad3[gi0 + 2] * z0);", + " }", + " let t1 = 0.6 - x1 * x1 - y1 * y1 - z1 * z1;", + " if (t1 < 0)", + " n1 = 0.0;", + " else {", + " const gi1 = permMod12[ii + i1 + perm[jj + j1 + perm[kk + k1]]] * 3;", + " t1 *= t1;", + " n1 = t1 * t1 * (grad3[gi1] * x1 + grad3[gi1 + 1] * y1 + grad3[gi1 + 2] * z1);", + " }", + " let t2 = 0.6 - x2 * x2 - y2 * y2 - z2 * z2;", + " if (t2 < 0)", + " n2 = 0.0;", + " else {", + " const gi2 = permMod12[ii + i2 + perm[jj + j2 + perm[kk + k2]]] * 3;", + " t2 *= t2;", + " n2 = t2 * t2 * (grad3[gi2] * x2 + grad3[gi2 + 1] * y2 + grad3[gi2 + 2] * z2);", + " }", + " let t3 = 0.6 - x3 * x3 - y3 * y3 - z3 * z3;", + " if (t3 < 0)", + " n3 = 0.0;", + " else {", + " const gi3 = permMod12[ii + 1 + perm[jj + 1 + perm[kk + 1]]] * 3;", + " t3 *= t3;", + " n3 = t3 * t3 * (grad3[gi3] * x3 + grad3[gi3 + 1] * y3 + grad3[gi3 + 2] * z3);", + " }", + " return 32.0 * (n0 + n1 + n2 + n3);", + " }", + " noise4D(x, y, z, w) {", + " const perm = this.perm;", + " let n0, n1, n2, n3, n4;", + " const s = (x + y + z + w) * F4;", + " const i = Math.floor(x + s);", + " const j = Math.floor(y + s);", + " const k = Math.floor(z + s);", + " const l = Math.floor(w + s);", + " const t = (i + j + k + l) * G4;", + " const X0 = i - t;", + " const Y0 = j - t;", + " const Z0 = k - t;", + " const W0 = l - t;", + " const x0 = x - X0;", + " const y0 = y - Y0;", + " const z0 = z - Z0;", + " const w0 = w - W0;", + " let rankx = 0;", + " let ranky = 0;", + " let rankz = 0;", + " let rankw = 0;", + " if (x0 > y0)", + " rankx++;", + " else", + " ranky++;", + " if (x0 > z0)", + " rankx++;", + " else", + " rankz++;", + " if (x0 > w0)", + " rankx++;", + " else", + " rankw++;", + " if (y0 > z0)", + " ranky++;", + " else", + " rankz++;", + " if (y0 > w0)", + " ranky++;", + " else", + " rankw++;", + " if (z0 > w0)", + " rankz++;", + " else", + " rankw++;", + " const i1 = rankx >= 3 ? 1 : 0;", + " const j1 = ranky >= 3 ? 1 : 0;", + " const k1 = rankz >= 3 ? 1 : 0;", + " const l1 = rankw >= 3 ? 1 : 0;", + " const i2 = rankx >= 2 ? 1 : 0;", + " const j2 = ranky >= 2 ? 1 : 0;", + " const k2 = rankz >= 2 ? 1 : 0;", + " const l2 = rankw >= 2 ? 1 : 0;", + " const i3 = rankx >= 1 ? 1 : 0;", + " const j3 = ranky >= 1 ? 1 : 0;", + " const k3 = rankz >= 1 ? 1 : 0;", + " const l3 = rankw >= 1 ? 1 : 0;", + " const x1 = x0 - i1 + G4;", + " const y1 = y0 - j1 + G4;", + " const z1 = z0 - k1 + G4;", + " const w1 = w0 - l1 + G4;", + " const x2 = x0 - i2 + 2.0 * G4;", + " const y2 = y0 - j2 + 2.0 * G4;", + " const z2 = z0 - k2 + 2.0 * G4;", + " const w2 = w0 - l2 + 2.0 * G4;", + " const x3 = x0 - i3 + 3.0 * G4;", + " const y3 = y0 - j3 + 3.0 * G4;", + " const z3 = z0 - k3 + 3.0 * G4;", + " const w3 = w0 - l3 + 3.0 * G4;", + " const x4 = x0 - 1.0 + 4.0 * G4;", + " const y4 = y0 - 1.0 + 4.0 * G4;", + " const z4 = z0 - 1.0 + 4.0 * G4;", + " const w4 = w0 - 1.0 + 4.0 * G4;", + " const ii = i & 255;", + " const jj = j & 255;", + " const kk = k & 255;", + " const ll = l & 255;", + " let t0 = 0.6 - x0 * x0 - y0 * y0 - z0 * z0 - w0 * w0;", + " if (t0 < 0)", + " n0 = 0.0;", + " else {", + " const gi0 = (perm[ii + perm[jj + perm[kk + perm[ll]]]] % 32) * 4;", + " t0 *= t0;", + " n0 = t0 * t0 * (grad4[gi0] * x0 + grad4[gi0 + 1] * y0 + grad4[gi0 + 2] * z0 + grad4[gi0 + 3] * w0);", + " }", + " let t1 = 0.6 - x1 * x1 - y1 * y1 - z1 * z1 - w1 * w1;", + " if (t1 < 0)", + " n1 = 0.0;", + " else {", + " const gi1 = (perm[ii + i1 + perm[jj + j1 + perm[kk + k1 + perm[ll + l1]]]] % 32) * 4;", + " t1 *= t1;", + " n1 = t1 * t1 * (grad4[gi1] * x1 + grad4[gi1 + 1] * y1 + grad4[gi1 + 2] * z1 + grad4[gi1 + 3] * w1);", + " }", + " let t2 = 0.6 - x2 * x2 - y2 * y2 - z2 * z2 - w2 * w2;", + " if (t2 < 0)", + " n2 = 0.0;", + " else {", + " const gi2 = (perm[ii + i2 + perm[jj + j2 + perm[kk + k2 + perm[ll + l2]]]] % 32) * 4;", + " t2 *= t2;", + " n2 = t2 * t2 * (grad4[gi2] * x2 + grad4[gi2 + 1] * y2 + grad4[gi2 + 2] * z2 + grad4[gi2 + 3] * w2);", + " }", + " let t3 = 0.6 - x3 * x3 - y3 * y3 - z3 * z3 - w3 * w3;", + " if (t3 < 0)", + " n3 = 0.0;", + " else {", + " const gi3 = (perm[ii + i3 + perm[jj + j3 + perm[kk + k3 + perm[ll + l3]]]] % 32) * 4;", + " t3 *= t3;", + " n3 = t3 * t3 * (grad4[gi3] * x3 + grad4[gi3 + 1] * y3 + grad4[gi3 + 2] * z3 + grad4[gi3 + 3] * w3);", + " }", + " let t4 = 0.6 - x4 * x4 - y4 * y4 - z4 * z4 - w4 * w4;", + " if (t4 < 0)", + " n4 = 0.0;", + " else {", + " const gi4 = (perm[ii + 1 + perm[jj + 1 + perm[kk + 1 + perm[ll + 1]]]] % 32) * 4;", + " t4 *= t4;", + " n4 = t4 * t4 * (grad4[gi4] * x4 + grad4[gi4 + 1] * y4 + grad4[gi4 + 2] * z4 + grad4[gi4 + 3] * w4);", + " }", + " return 27.0 * (n0 + n1 + n2 + n3 + n4);", + " }", + "}", + "function buildPermutationTable(random) {", + " const p = new Uint8Array(256);", + " for (let i = 0; i < 256; i++) {", + " p[i] = i;", + " }", + " for (let i = 0; i < 255; i++) {", + " const r = i + ~~(random() * (256 - i));", + " const aux = p[i];", + " p[i] = p[r];", + " p[r] = aux;", + " }", + " return p;", + "}", + "function alea(seed) {", + " let s0 = 0;", + " let s1 = 0;", + " let s2 = 0;", + " let c = 1;", + " const mash = masher();", + " s0 = mash(' ');", + " s1 = mash(' ');", + " s2 = mash(' ');", + " s0 -= mash(seed);", + " if (s0 < 0) {", + " s0 += 1;", + " }", + " s1 -= mash(seed);", + " if (s1 < 0) {", + " s1 += 1;", + " }", + " s2 -= mash(seed);", + " if (s2 < 0) {", + " s2 += 1;", + " }", + " return function () {", + " const t = 2091639 * s0 + c * 2.3283064365386963e-10;", + " s0 = s1;", + " s1 = s2;", + " return s2 = t - (c = t | 0);", + " };", + "}", + "function masher() {", + " let n = 0xefc8249d;", + " return function (data) {", + " data = data.toString();", + " for (let i = 0; i < data.length; i++) {", + " n += data.charCodeAt(i);", + " let h = 0.02519603282416938 * n;", + " n = h >>> 0;", + " h -= n;", + " h *= n;", + " n = h >>> 0;", + " h -= n;", + " n += h * 0x100000000;", + " }", + " return (n >>> 0) * 2.3283064365386963e-10;", + " };", + "}", + "", + "class TurbulenceField {", + " constructor(scale, octaves, velocityMultiplier, timeScale) {", + " this.scale = scale;", + " this.octaves = octaves;", + " this.velocityMultiplier = velocityMultiplier;", + " this.timeScale = timeScale;", + " this.type = 'TurbulenceField';", + " this.generator = new SimplexNoise();", + " this.timeOffset = new THREE.Vector3();", + " this.temp = new THREE.Vector3();", + " this.temp2 = new THREE.Vector3();", + " this.timeOffset.x = Math.random() / this.scale.x * this.timeScale.x;", + " this.timeOffset.y = Math.random() / this.scale.y * this.timeScale.y;", + " this.timeOffset.z = Math.random() / this.scale.z * this.timeScale.z;", + " }", + " initialize(particle) {", + " }", + " update(particle, delta) {", + " const x = particle.position.x / this.scale.x;", + " const y = particle.position.y / this.scale.y;", + " const z = particle.position.z / this.scale.z;", + " this.temp.set(0, 0, 0);", + " let lvl = 1;", + " for (let i = 0; i < this.octaves; i++) {", + " this.temp2.set(this.generator.noise4D(x * lvl, y * lvl, z * lvl, this.timeOffset.x * lvl) / lvl, this.generator.noise4D(x * lvl, y * lvl, z * lvl, this.timeOffset.y * lvl) / lvl, this.generator.noise4D(x * lvl, y * lvl, z * lvl, this.timeOffset.z * lvl) / lvl);", + " this.temp.add(this.temp2);", + " lvl *= 2;", + " }", + " this.temp.multiply(this.velocityMultiplier);", + " particle.velocity.addScaledVector(this.temp, delta);", + " }", + " toJSON() {", + " return {", + " type: this.type,", + " scale: [this.scale.x, this.scale.y, this.scale.z],", + " octaves: this.octaves,", + " velocityMultiplier: [this.velocityMultiplier.x, this.velocityMultiplier.y, this.velocityMultiplier.z],", + " timeScale: [this.timeScale.x, this.timeScale.y, this.timeScale.z],", + " };", + " }", + " frameUpdate(delta) {", + " this.timeOffset.x += delta * this.timeScale.x;", + " this.timeOffset.y += delta * this.timeScale.y;", + " this.timeOffset.z += delta * this.timeScale.z;", + " }", + " static fromJSON(json) {", + " return new TurbulenceField(new THREE.Vector3(json.scale[0], json.scale[1], json.scale[2]), json.octaves, new THREE.Vector3(json.velocityMultiplier[0], json.velocityMultiplier[1], json.velocityMultiplier[2]), new THREE.Vector3(json.timeScale[0], json.timeScale[1], json.timeScale[2]));", + " }", + " clone() {", + " return new TurbulenceField(this.scale.clone(), this.octaves, this.velocityMultiplier.clone(), this.timeScale.clone());", + " }", + " reset() {", + " }", + "}", + "", + "function randomInt(a, b) {", + " return Math.floor(Math.random() * (b - a)) + a;", + "}", + "", + "const generators = [];", + "const tempV$2 = new THREE.Vector3();", + "const tempQ$2 = new THREE.Quaternion();", + "class Noise {", + " constructor(frequency, power, positionAmount = new ConstantValue(1), rotationAmount = new ConstantValue(0)) {", + " this.frequency = frequency;", + " this.power = power;", + " this.positionAmount = positionAmount;", + " this.rotationAmount = rotationAmount;", + " this.type = 'Noise';", + " this.duration = 0;", + " if (generators.length === 0) {", + " for (let i = 0; i < 100; i++) {", + " generators.push(new SimplexNoise());", + " }", + " }", + " }", + " initialize(particle) {", + " particle.lastPosNoise = new THREE.Vector3();", + " if (typeof particle.rotation === 'number') {", + " particle.lastRotNoise = 0;", + " }", + " else {", + " particle.lastRotNoise = new THREE.Quaternion();", + " }", + " particle.generatorIndex = [randomInt(0, 100), randomInt(0, 100), randomInt(0, 100), randomInt(0, 100)];", + " }", + " update(particle, _) {", + " let frequency = this.frequency.genValue(particle.age / particle.life);", + " let power = this.power.genValue(particle.age / particle.life);", + " let positionAmount = this.positionAmount.genValue(particle.age / particle.life);", + " let rotationAmount = this.rotationAmount.genValue(particle.age / particle.life);", + " if (positionAmount > 0 && particle.lastPosNoise !== undefined) {", + " particle.position.sub(particle.lastPosNoise);", + " tempV$2.set(generators[particle.generatorIndex[0]].noise2D(0, particle.age * frequency) *", + " power *", + " positionAmount, generators[particle.generatorIndex[1]].noise2D(0, particle.age * frequency) *", + " power *", + " positionAmount, generators[particle.generatorIndex[2]].noise2D(0, particle.age * frequency) *", + " power *", + " positionAmount);", + " particle.position.add(tempV$2);", + " particle.lastPosNoise.copy(tempV$2);", + " }", + " if (rotationAmount > 0 && particle.lastRotNoise !== undefined) {", + " if (typeof particle.rotation === 'number') {", + " particle.rotation -= particle.lastRotNoise;", + " particle.rotation +=", + " generators[particle.generatorIndex[3]].noise2D(0, particle.age * frequency) *", + " Math.PI *", + " power *", + " rotationAmount;", + " }", + " else {", + " particle.lastRotNoise.invert();", + " particle.rotation.multiply(particle.lastRotNoise);", + " tempQ$2", + " .set(generators[particle.generatorIndex[0]].noise2D(0, particle.age * frequency) *", + " power *", + " rotationAmount, generators[particle.generatorIndex[1]].noise2D(0, particle.age * frequency) *", + " power *", + " rotationAmount, generators[particle.generatorIndex[2]].noise2D(0, particle.age * frequency) *", + " power *", + " rotationAmount, generators[particle.generatorIndex[3]].noise2D(0, particle.age * frequency) *", + " power *", + " rotationAmount)", + " .normalize();", + " particle.rotation.multiply(tempQ$2);", + " particle.lastRotNoise.copy(tempQ$2);", + " }", + " }", + " }", + " toJSON() {", + " return {", + " type: this.type,", + " frequency: this.frequency.toJSON(),", + " power: this.power.toJSON(),", + " positionAmount: this.positionAmount.toJSON(),", + " rotationAmount: this.rotationAmount.toJSON(),", + " };", + " }", + " frameUpdate(delta) {", + " this.duration += delta;", + " }", + " static fromJSON(json) {", + " return new Noise(ValueGeneratorFromJSON(json.frequency), ValueGeneratorFromJSON(json.power), ValueGeneratorFromJSON(json.positionAmount), ValueGeneratorFromJSON(json.rotationAmount));", + " }", + " clone() {", + " return new Noise(this.frequency.clone(), this.power.clone(), this.positionAmount.clone(), this.rotationAmount.clone());", + " }", + " reset() { }", + "}", + "", + "class TextureSequencer {", + " constructor(scaleX = 0, scaleY = 0, position = new THREE.Vector3()) {", + " this.scaleX = scaleX;", + " this.scaleY = scaleY;", + " this.position = position;", + " this.locations = [];", + " }", + " transform(position, index) {", + " position.x = this.locations[index % this.locations.length].x * this.scaleX + this.position.x;", + " position.y = this.locations[index % this.locations.length].y * this.scaleY + this.position.y;", + " position.z = this.position.z;", + " }", + " static fromJSON(json) {", + " const textureSequencer = new TextureSequencer(json.scaleX, json.scaleY, new THREE.Vector3(json.position[0], json.position[1], json.position[2]));", + " textureSequencer.locations = json.locations.map((loc) => new THREE.Vector2(loc.x, loc.y));", + " return textureSequencer;", + " }", + " clone() {", + " const textureSequencer = new TextureSequencer(this.scaleX, this.scaleY, this.position.clone());", + " textureSequencer.locations = this.locations.map(loc => loc.clone());", + " return textureSequencer;", + " }", + " toJSON() {", + " return {", + " scaleX: this.scaleX,", + " scaleY: this.scaleY,", + " position: this.position,", + " locations: this.locations.map(loc => ({", + " x: loc.x,", + " y: loc.y,", + " }))", + " };", + " }", + " fromImage(img, threshold) {", + " const canvas = document.createElement(\"canvas\");", + " canvas.width = img.width;", + " canvas.height = img.height;", + " const ctx = canvas.getContext(\"2d\");", + " if (!ctx) {", + " return;", + " }", + " ctx.drawImage(img, 0, 0);", + " const data = ctx.getImageData(0, 0, canvas.width, canvas.height, { colorSpace: \"srgb\" });", + " canvas.remove();", + " this.locations.length = 0;", + " for (let i = 0; i < data.height; i++) {", + " for (let j = 0; j < data.width; j++) {", + " if (data.data[(i * data.width + j) * 4 + 3] > threshold) {", + " this.locations.push(new THREE.Vector2(j, data.height - i));", + " }", + " }", + " }", + " }", + "}", + "", + "function SequencerFromJSON(json) {", + " switch (json.type) {", + " case 'TextureSequencer':", + " return TextureSequencer.fromJSON(json);", + " default:", + " return new TextureSequencer();", + " }", + "}", + "", + "class ApplySequences {", + " constructor(delayBetweenParticles) {", + " this.type = 'ApplySequences';", + " this.sequencers = [];", + " this.time = 0;", + " this.index = 0;", + " this.pCount = 0;", + " this.tempV = new THREE.Vector3();", + " this.delay = delayBetweenParticles;", + " }", + " initialize(particle) {", + " particle.id = this.pCount;", + " particle.dst = new THREE.Vector3();", + " particle.begin = new THREE.Vector3();", + " particle.inMotion = false;", + " this.pCount++;", + " }", + " reset() {", + " this.time = 0;", + " this.index = 0;", + " this.pCount = 0;", + " }", + " update(particle, delta) {", + " const sequencer = this.sequencers[this.index];", + " const delay = particle.id * this.delay;", + " if (this.time >= sequencer[0].a + delay && this.time <= sequencer[0].b + delay) {", + " if (!particle.inMotion) {", + " particle.inMotion = true;", + " particle.begin.copy(particle.position);", + " sequencer[1].transform(particle.dst, particle.id);", + " }", + " particle.position.lerpVectors(particle.begin, particle.dst, ApplySequences.BEZIER.genValue((this.time - sequencer[0].a - delay) / (sequencer[0].b - sequencer[0].a)));", + " }", + " else if (this.time > sequencer[0].b + delay) {", + " particle.inMotion = false;", + " }", + " }", + " frameUpdate(delta) {", + " while (this.index + 1 < this.sequencers.length && this.time >= this.sequencers[this.index + 1][0].a) {", + " this.index++;", + " }", + " this.time += delta;", + " }", + " appendSequencer(range, sequencer) {", + " this.sequencers.push([range, sequencer]);", + " }", + " toJSON() {", + " return {", + " type: this.type,", + " delay: this.delay,", + " sequencers: this.sequencers.map(([range, sequencer]) => ({", + " range: range.toJSON(),", + " sequencer: sequencer.toJSON(),", + " })),", + " };", + " }", + " static fromJSON(json) {", + " const seq = new ApplySequences(json.delay);", + " json.sequencers.forEach((sequencerJson) => {", + " seq.sequencers.push([ValueGeneratorFromJSON(sequencerJson.range), SequencerFromJSON(sequencerJson.sequencer)]);", + " });", + " return seq;", + " }", + " clone() {", + " const applySequences = new ApplySequences(this.delay);", + " applySequences.sequencers = this.sequencers.map(seq => [seq[0].clone(), seq[1].clone()]);", + " return applySequences;", + " }", + "}", + "ApplySequences.BEZIER = new Bezier(0, 0, 1, 1);", + "", + "let physicsResolver;", + "function setPhysicsResolver(resolver) {", + " physicsResolver = resolver;", + "}", + "function getPhysicsResolver() {", + " return physicsResolver;", + "}", + "class ApplyCollision {", + " constructor(resolver, bounce) {", + " this.resolver = resolver;", + " this.bounce = bounce;", + " this.type = 'ApplyCollision';", + " this.tempV = new THREE.Vector3();", + " }", + " initialize(particle) {", + " }", + " update(particle, delta) {", + " if (this.resolver.resolve(particle.position, this.tempV)) {", + " particle.velocity.reflect(this.tempV).multiplyScalar(this.bounce);", + " }", + " }", + " frameUpdate(delta) {", + " }", + " toJSON() {", + " return {", + " type: this.type,", + " bounce: this.bounce,", + " };", + " }", + " static fromJSON(json) {", + " return new ApplyCollision(getPhysicsResolver(), json.bounce);", + " }", + " clone() {", + " return new ApplyCollision(this.resolver, this.bounce);", + " }", + " reset() {", + " }", + "}", + "", + "class ColorBySpeed {", + " constructor(color, speedRange) {", + " this.color = color;", + " this.speedRange = speedRange;", + " this.type = 'ColorBySpeed';", + " }", + " initialize(particle) {", + " if (this.color.type === 'memorizedFunction') {", + " particle.colorOverLifeMemory = {};", + " this.color.startGen(particle.colorOverLifeMemory);", + " }", + " }", + " update(particle, delta) {", + " const t = (particle.startSpeed - this.speedRange.a) / (this.speedRange.b - this.speedRange.a);", + " if (this.color.type === 'memorizedFunction') {", + " this.color.genColor(particle.color, t, particle.colorOverLifeMemory);", + " }", + " else {", + " this.color.genColor(particle.color, t);", + " }", + " particle.color.x *= particle.startColor.x;", + " particle.color.y *= particle.startColor.y;", + " particle.color.z *= particle.startColor.z;", + " particle.color.w *= particle.startColor.w;", + " }", + " frameUpdate(delta) { }", + " toJSON() {", + " return {", + " type: this.type,", + " color: this.color.toJSON(),", + " speedRange: this.speedRange.toJSON(),", + " };", + " }", + " static fromJSON(json) {", + " return new ColorBySpeed(ColorGeneratorFromJSON(json.color), IntervalValue.fromJSON(json.speedRange));", + " }", + " clone() {", + " return new ColorBySpeed(this.color.clone(), this.speedRange.clone());", + " }", + " reset() { }", + "}", + "", + "class SizeBySpeed {", + " initialize(particle) { }", + " constructor(size, speedRange) {", + " this.size = size;", + " this.speedRange = speedRange;", + " this.type = 'SizeBySpeed';", + " }", + " update(particle) {", + " const t = (particle.startSpeed - this.speedRange.a) / (this.speedRange.b - this.speedRange.a);", + " particle.size = particle.startSize * this.size.genValue(t);", + " }", + " toJSON() {", + " return {", + " type: this.type,", + " size: this.size.toJSON(),", + " speedRange: this.speedRange.toJSON(),", + " };", + " }", + " static fromJSON(json) {", + " return new SizeBySpeed(ValueGeneratorFromJSON(json.size), IntervalValue.fromJSON(json.speedRange));", + " }", + " frameUpdate(delta) { }", + " clone() {", + " return new SizeBySpeed(this.size.clone(), this.speedRange.clone());", + " }", + " reset() { }", + "}", + "", + "class RotationBySpeed {", + " constructor(angularVelocity, speedRange) {", + " this.angularVelocity = angularVelocity;", + " this.speedRange = speedRange;", + " this.type = 'RotationBySpeed';", + " this.tempQuat = new THREE.Quaternion();", + " }", + " initialize(particle) { }", + " update(particle, delta) {", + " const t = (particle.startSpeed - this.speedRange.a) / (this.speedRange.b - this.speedRange.a);", + " particle.rotation += delta * this.angularVelocity.genValue(t);", + " }", + " toJSON() {", + " return {", + " type: this.type,", + " angularVelocity: this.angularVelocity.toJSON(),", + " speedRange: this.speedRange.toJSON(),", + " };", + " }", + " static fromJSON(json) {", + " return new RotationBySpeed(ValueGeneratorFromJSON(json.angularVelocity), IntervalValue.fromJSON(json.speedRange));", + " }", + " frameUpdate(delta) { }", + " clone() {", + " return new RotationBySpeed(this.angularVelocity.clone(), this.speedRange.clone());", + " }", + " reset() { }", + "}", + "", + "class LimitSpeedOverLife {", + " initialize(particle) { }", + " constructor(speed, dampen) {", + " this.speed = speed;", + " this.dampen = dampen;", + " this.type = 'LimitSpeedOverLife';", + " }", + " update(particle, delta) {", + " let speed = particle.velocity.length();", + " let limit = this.speed.genValue(particle.age / particle.life);", + " if (speed > limit) {", + " const percent = (speed - limit) / speed;", + " particle.velocity.multiplyScalar(1 - percent * this.dampen * delta * 20);", + " }", + " }", + " toJSON() {", + " return {", + " type: this.type,", + " speed: this.speed.toJSON(),", + " dampen: this.dampen,", + " };", + " }", + " static fromJSON(json) {", + " return new LimitSpeedOverLife(ValueGeneratorFromJSON(json.speed), json.dampen);", + " }", + " frameUpdate(delta) { }", + " clone() {", + " return new LimitSpeedOverLife(this.speed.clone(), this.dampen);", + " }", + " reset() { }", + "}", + "", + "const BehaviorTypes = {", + " ApplyForce: {", + " type: 'ApplyForce',", + " constructor: ApplyForce,", + " params: [", + " ['direction', 'vec3'],", + " ['magnitude', 'value'],", + " ],", + " loadJSON: ApplyForce.fromJSON,", + " },", + " Noise: {", + " type: 'Noise',", + " constructor: Noise,", + " params: [", + " ['frequency', 'value'],", + " ['power', 'value'],", + " ['positionAmount', 'value'],", + " ['rotationAmount', 'value'],", + " ],", + " loadJSON: Noise.fromJSON,", + " },", + " TurbulenceField: {", + " type: 'TurbulenceField',", + " constructor: TurbulenceField,", + " params: [", + " ['scale', 'vec3'],", + " ['octaves', 'number'],", + " ['velocityMultiplier', 'vec3'],", + " ['timeScale', 'vec3'],", + " ],", + " loadJSON: TurbulenceField.fromJSON,", + " },", + " GravityForce: {", + " type: 'GravityForce',", + " constructor: GravityForce,", + " params: [", + " ['center', 'vec3'],", + " ['magnitude', 'number'],", + " ],", + " loadJSON: GravityForce.fromJSON,", + " },", + " ColorOverLife: {", + " type: 'ColorOverLife',", + " constructor: ColorOverLife,", + " params: [['color', 'colorFunc']],", + " loadJSON: ColorOverLife.fromJSON,", + " },", + " RotationOverLife: {", + " type: 'RotationOverLife',", + " constructor: RotationOverLife,", + " params: [['angularVelocity', 'valueFunc']],", + " loadJSON: RotationOverLife.fromJSON,", + " },", + " Rotation3DOverLife: {", + " type: 'Rotation3DOverLife',", + " constructor: Rotation3DOverLife,", + " params: [['angularVelocity', 'rotationFunc']],", + " loadJSON: Rotation3DOverLife.fromJSON,", + " },", + " SizeOverLife: {", + " type: 'SizeOverLife',", + " constructor: SizeOverLife,", + " params: [['size', 'valueFunc']],", + " loadJSON: SizeOverLife.fromJSON,", + " },", + " ColorBySpeed: {", + " type: 'ColorBySpeed',", + " constructor: ColorBySpeed,", + " params: [", + " ['color', 'colorFunc'],", + " ['speedRange', 'range'],", + " ],", + " loadJSON: ColorBySpeed.fromJSON,", + " },", + " RotationBySpeed: {", + " type: 'RotationBySpeed',", + " constructor: RotationBySpeed,", + " params: [", + " ['angularVelocity', 'valueFunc'],", + " ['speedRange', 'range'],", + " ],", + " loadJSON: RotationBySpeed.fromJSON,", + " },", + " SizeBySpeed: {", + " type: 'SizeBySpeed',", + " constructor: SizeBySpeed,", + " params: [", + " ['size', 'valueFunc'],", + " ['speedRange', 'range'],", + " ],", + " loadJSON: SizeBySpeed.fromJSON,", + " },", + " SpeedOverLife: {", + " type: 'SpeedOverLife',", + " constructor: SpeedOverLife,", + " params: [['speed', 'valueFunc']],", + " loadJSON: SpeedOverLife.fromJSON,", + " },", + " FrameOverLife: {", + " type: 'FrameOverLife',", + " constructor: FrameOverLife,", + " params: [['frame', 'valueFunc']],", + " loadJSON: FrameOverLife.fromJSON,", + " },", + " ForceOverLife: {", + " type: 'ForceOverLife',", + " constructor: ForceOverLife,", + " params: [", + " ['x', 'valueFunc'],", + " ['y', 'valueFunc'],", + " ['z', 'valueFunc'],", + " ],", + " loadJSON: ForceOverLife.fromJSON,", + " },", + " OrbitOverLife: {", + " type: 'OrbitOverLife',", + " constructor: OrbitOverLife,", + " params: [", + " ['orbitSpeed', 'valueFunc'],", + " ['axis', 'vec3'],", + " ],", + " loadJSON: OrbitOverLife.fromJSON,", + " },", + " WidthOverLength: {", + " type: 'WidthOverLength',", + " constructor: WidthOverLength,", + " params: [['width', 'valueFunc']],", + " loadJSON: WidthOverLength.fromJSON,", + " },", + " ChangeEmitDirection: {", + " type: 'ChangeEmitDirection',", + " constructor: ChangeEmitDirection,", + " params: [['angle', 'value']],", + " loadJSON: ChangeEmitDirection.fromJSON,", + " },", + " EmitSubParticleSystem: {", + " type: 'EmitSubParticleSystem',", + " constructor: EmitSubParticleSystem,", + " params: [", + " ['particleSystem', 'self'],", + " ['useVelocityAsBasis', 'boolean'],", + " ['subParticleSystem', 'particleSystem'],", + " ['mode', 'number'],", + " ['emitProbability', 'number'],", + " ],", + " loadJSON: EmitSubParticleSystem.fromJSON,", + " },", + " LimitSpeedOverLife: {", + " type: 'LimitSpeedOverLife',", + " constructor: LimitSpeedOverLife,", + " params: [", + " ['speed', 'valueFunc'],", + " ['dampen', 'number'],", + " ],", + " loadJSON: LimitSpeedOverLife.fromJSON,", + " },", + "};", + "function BehaviorFromJSON(json, particleSystem) {", + " return BehaviorTypes[json.type].loadJSON(json, particleSystem);", + "}", + "", + "let EmitterMode = void 0;", + "(function (EmitterMode) {", + " EmitterMode[EmitterMode[\"Random\"] = 0] = \"Random\";", + " EmitterMode[EmitterMode[\"Loop\"] = 1] = \"Loop\";", + " EmitterMode[EmitterMode[\"PingPong\"] = 2] = \"PingPong\";", + " EmitterMode[EmitterMode[\"Burst\"] = 3] = \"Burst\";", + "})(EmitterMode || (EmitterMode = {}));", + "function getValueFromEmitterMode(mode, currentValue, spread) {", + " let u;", + " if (EmitterMode.Random === mode) {", + " currentValue = Math.random();", + " }", + " if (spread > 0) {", + " u = Math.floor(currentValue / spread) * spread;", + " }", + " else {", + " u = currentValue;", + " }", + " switch (mode) {", + " case EmitterMode.Loop:", + " u = u % 1;", + " break;", + " case EmitterMode.PingPong:", + " u = Math.abs((u % 2) - 1);", + " break;", + " }", + " return u;", + "}", + "", + "class ConeEmitter {", + " constructor(parameters = {}) {", + " var _a, _b, _c, _d, _e, _f, _g;", + " this.type = 'cone';", + " this.currentValue = 0;", + " this.radius = (_a = parameters.radius) !== null && _a !== void 0 ? _a : 10;", + " this.arc = (_b = parameters.arc) !== null && _b !== void 0 ? _b : 2.0 * Math.PI;", + " this.thickness = (_c = parameters.thickness) !== null && _c !== void 0 ? _c : 1;", + " this.angle = (_d = parameters.angle) !== null && _d !== void 0 ? _d : Math.PI / 6;", + " this.mode = (_e = parameters.mode) !== null && _e !== void 0 ? _e : EmitterMode.Random;", + " this.spread = (_f = parameters.spread) !== null && _f !== void 0 ? _f : 0;", + " this.speed = (_g = parameters.speed) !== null && _g !== void 0 ? _g : new ConstantValue(1);", + " }", + " update(system, delta) {", + " if (EmitterMode.Random != this.mode) {", + " this.currentValue += this.speed.genValue(system.emissionState.time / system.duration) * delta;", + " }", + " }", + " initialize(p) {", + " const u = getValueFromEmitterMode(this.mode, this.currentValue, this.spread);", + " const rand = THREE.MathUtils.lerp(1 - this.thickness, 1, Math.random());", + " const theta = u * this.arc;", + " const r = Math.sqrt(rand);", + " const sinTheta = Math.sin(theta);", + " const cosTheta = Math.cos(theta);", + " p.position.x = r * cosTheta;", + " p.position.y = r * sinTheta;", + " p.position.z = 0;", + " const angle = this.angle * r;", + " p.velocity.set(0, 0, Math.cos(angle)).addScaledVector(p.position, Math.sin(angle)).multiplyScalar(p.startSpeed);", + " p.position.multiplyScalar(this.radius);", + " }", + " toJSON() {", + " return {", + " type: 'cone',", + " radius: this.radius,", + " arc: this.arc,", + " thickness: this.thickness,", + " angle: this.angle,", + " mode: this.mode,", + " spread: this.spread,", + " speed: this.speed.toJSON(),", + " };", + " }", + " static fromJSON(json) {", + " return new ConeEmitter({", + " radius: json.radius,", + " arc: json.arc,", + " thickness: json.thickness,", + " angle: json.angle,", + " mode: json.mode,", + " speed: json.speed ? ValueGeneratorFromJSON(json.speed) : undefined,", + " spread: json.spread,", + " });", + " }", + " clone() {", + " return new ConeEmitter({", + " radius: this.radius,", + " arc: this.arc,", + " thickness: this.thickness,", + " angle: this.angle,", + " mode: this.mode,", + " speed: this.speed.clone(),", + " spread: this.spread,", + " });", + " }", + "}", + "", + "class CircleEmitter {", + " constructor(parameters = {}) {", + " var _a, _b, _c, _d, _e, _f;", + " this.type = 'circle';", + " this.currentValue = 0;", + " this.radius = (_a = parameters.radius) !== null && _a !== void 0 ? _a : 10;", + " this.arc = (_b = parameters.arc) !== null && _b !== void 0 ? _b : 2.0 * Math.PI;", + " this.thickness = (_c = parameters.thickness) !== null && _c !== void 0 ? _c : 1;", + " this.mode = (_d = parameters.mode) !== null && _d !== void 0 ? _d : EmitterMode.Random;", + " this.spread = (_e = parameters.spread) !== null && _e !== void 0 ? _e : 0;", + " this.speed = (_f = parameters.speed) !== null && _f !== void 0 ? _f : new ConstantValue(1);", + " }", + " update(system, delta) {", + " this.currentValue += this.speed.genValue(system.emissionState.time / system.duration) * delta;", + " }", + " initialize(p) {", + " const u = getValueFromEmitterMode(this.mode, this.currentValue, this.spread);", + " const r = THREE.MathUtils.lerp(1 - this.thickness, 1, Math.random());", + " const theta = u * this.arc;", + " p.position.x = Math.cos(theta);", + " p.position.y = Math.sin(theta);", + " p.position.z = 0;", + " p.velocity.copy(p.position).multiplyScalar(p.startSpeed);", + " p.position.multiplyScalar(this.radius * r);", + " }", + " toJSON() {", + " return {", + " type: 'circle',", + " radius: this.radius,", + " arc: this.arc,", + " thickness: this.thickness,", + " mode: this.mode,", + " spread: this.spread,", + " speed: this.speed.toJSON(),", + " };", + " }", + " static fromJSON(json) {", + " return new CircleEmitter({", + " radius: json.radius,", + " arc: json.arc,", + " thickness: json.thickness,", + " mode: json.mode,", + " speed: json.speed ? ValueGeneratorFromJSON(json.speed) : undefined,", + " spread: json.spread,", + " });", + " }", + " clone() {", + " return new CircleEmitter({", + " radius: this.radius,", + " arc: this.arc,", + " thickness: this.thickness,", + " mode: this.mode,", + " speed: this.speed.clone(),", + " spread: this.spread,", + " });", + " }", + "}", + "", + "class DonutEmitter {", + " constructor(parameters = {}) {", + " var _a, _b, _c, _d, _e, _f, _g;", + " this.type = 'donut';", + " this.currentValue = 0;", + " this.radius = (_a = parameters.radius) !== null && _a !== void 0 ? _a : 10;", + " this.arc = (_b = parameters.arc) !== null && _b !== void 0 ? _b : 2.0 * Math.PI;", + " this.thickness = (_c = parameters.thickness) !== null && _c !== void 0 ? _c : 1;", + " this.donutRadius = (_d = parameters.donutRadius) !== null && _d !== void 0 ? _d : this.radius * 0.2;", + " this.mode = (_e = parameters.mode) !== null && _e !== void 0 ? _e : EmitterMode.Random;", + " this.spread = (_f = parameters.spread) !== null && _f !== void 0 ? _f : 0;", + " this.speed = (_g = parameters.speed) !== null && _g !== void 0 ? _g : new ConstantValue(1);", + " }", + " update(system, delta) {", + " if (EmitterMode.Random != this.mode) {", + " this.currentValue += this.speed.genValue(system.emissionState.time / system.duration) * delta;", + " }", + " }", + " initialize(p) {", + " const u = getValueFromEmitterMode(this.mode, this.currentValue, this.spread);", + " const v = Math.random();", + " const rand = THREE.MathUtils.lerp(1 - this.thickness, 1, Math.random());", + " const theta = u * this.arc;", + " const phi = v * Math.PI * 2;", + " const sinTheta = Math.sin(theta);", + " const cosTheta = Math.cos(theta);", + " p.position.x = this.radius * cosTheta;", + " p.position.y = this.radius * sinTheta;", + " p.position.z = 0;", + " p.velocity.z = this.donutRadius * rand * Math.sin(phi);", + " p.velocity.x = this.donutRadius * rand * Math.cos(phi) * cosTheta;", + " p.velocity.y = this.donutRadius * rand * Math.cos(phi) * sinTheta;", + " p.position.add(p.velocity);", + " p.velocity.normalize().multiplyScalar(p.startSpeed);", + " }", + " toJSON() {", + " return {", + " type: 'donut',", + " radius: this.radius,", + " arc: this.arc,", + " thickness: this.thickness,", + " donutRadius: this.donutRadius,", + " mode: this.mode,", + " spread: this.spread,", + " speed: this.speed.toJSON(),", + " };", + " }", + " static fromJSON(json) {", + " return new DonutEmitter({", + " radius: json.radius,", + " arc: json.arc,", + " thickness: json.thickness,", + " donutRadius: json.donutRadius,", + " mode: json.mode,", + " speed: json.speed ? ValueGeneratorFromJSON(json.speed) : undefined,", + " spread: json.spread,", + " });", + " }", + " clone() {", + " return new DonutEmitter({", + " radius: this.radius,", + " arc: this.arc,", + " thickness: this.thickness,", + " donutRadius: this.donutRadius,", + " mode: this.mode,", + " speed: this.speed.clone(),", + " spread: this.spread,", + " });", + " }", + "}", + "", + "class PointEmitter {", + " constructor() {", + " this.type = 'point';", + " }", + " update(system, delta) { }", + " initialize(p) {", + " const u = Math.random();", + " const v = Math.random();", + " const theta = u * Math.PI * 2;", + " const phi = Math.acos(2.0 * v - 1.0);", + " const r = Math.cbrt(Math.random());", + " const sinTheta = Math.sin(theta);", + " const cosTheta = Math.cos(theta);", + " const sinPhi = Math.sin(phi);", + " const cosPhi = Math.cos(phi);", + " p.velocity.x = r * sinPhi * cosTheta;", + " p.velocity.y = r * sinPhi * sinTheta;", + " p.velocity.z = r * cosPhi;", + " p.velocity.multiplyScalar(p.startSpeed);", + " p.position.setScalar(0);", + " }", + " toJSON() {", + " return {", + " type: 'point',", + " };", + " }", + " static fromJSON(json) {", + " return new PointEmitter();", + " }", + " clone() {", + " return new PointEmitter();", + " }", + "}", + "", + "class SphereEmitter {", + " constructor(parameters = {}) {", + " var _a, _b, _c, _d, _e, _f;", + " this.type = 'sphere';", + " this.currentValue = 0;", + " this.radius = (_a = parameters.radius) !== null && _a !== void 0 ? _a : 10;", + " this.arc = (_b = parameters.arc) !== null && _b !== void 0 ? _b : 2.0 * Math.PI;", + " this.thickness = (_c = parameters.thickness) !== null && _c !== void 0 ? _c : 1;", + " this.mode = (_d = parameters.mode) !== null && _d !== void 0 ? _d : EmitterMode.Random;", + " this.spread = (_e = parameters.spread) !== null && _e !== void 0 ? _e : 0;", + " this.speed = (_f = parameters.speed) !== null && _f !== void 0 ? _f : new ConstantValue(1);", + " }", + " update(system, delta) {", + " if (EmitterMode.Random != this.mode) {", + " this.currentValue += this.speed.genValue(system.emissionState.time / system.duration) * delta;", + " }", + " }", + " initialize(p) {", + " const u = getValueFromEmitterMode(this.mode, this.currentValue, this.spread);", + " const v = Math.random();", + " const rand = THREE.MathUtils.lerp(1 - this.thickness, 1, Math.random());", + " const theta = u * this.arc;", + " const phi = Math.acos(2.0 * v - 1.0);", + " const sinTheta = Math.sin(theta);", + " const cosTheta = Math.cos(theta);", + " const sinPhi = Math.sin(phi);", + " const cosPhi = Math.cos(phi);", + " p.position.x = sinPhi * cosTheta;", + " p.position.y = sinPhi * sinTheta;", + " p.position.z = cosPhi;", + " p.velocity.copy(p.position).multiplyScalar(p.startSpeed);", + " p.position.multiplyScalar(this.radius * rand);", + " }", + " toJSON() {", + " return {", + " type: 'sphere',", + " radius: this.radius,", + " arc: this.arc,", + " thickness: this.thickness,", + " mode: this.mode,", + " spread: this.spread,", + " speed: this.speed.toJSON(),", + " };", + " }", + " static fromJSON(json) {", + " return new SphereEmitter({", + " radius: json.radius,", + " arc: json.arc,", + " thickness: json.thickness,", + " mode: json.mode,", + " speed: json.speed ? ValueGeneratorFromJSON(json.speed) : undefined,", + " spread: json.spread,", + " });", + " }", + " clone() {", + " return new SphereEmitter({", + " radius: this.radius,", + " arc: this.arc,", + " thickness: this.thickness,", + " mode: this.mode,", + " speed: this.speed.clone(),", + " spread: this.spread,", + " });", + " }", + "}", + "", + "class HemisphereEmitter {", + " constructor(parameters = {}) {", + " var _a, _b, _c, _d, _e, _f;", + " this.type = 'sphere';", + " this.currentValue = 0;", + " this.radius = (_a = parameters.radius) !== null && _a !== void 0 ? _a : 10;", + " this.arc = (_b = parameters.arc) !== null && _b !== void 0 ? _b : 2.0 * Math.PI;", + " this.thickness = (_c = parameters.thickness) !== null && _c !== void 0 ? _c : 1;", + " this.mode = (_d = parameters.mode) !== null && _d !== void 0 ? _d : EmitterMode.Random;", + " this.spread = (_e = parameters.spread) !== null && _e !== void 0 ? _e : 0;", + " this.speed = (_f = parameters.speed) !== null && _f !== void 0 ? _f : new ConstantValue(1);", + " }", + " update(system, delta) {", + " if (EmitterMode.Random != this.mode) {", + " this.currentValue += this.speed.genValue(system.emissionState.time / system.duration) * delta;", + " }", + " }", + " initialize(p) {", + " const u = getValueFromEmitterMode(this.mode, this.currentValue, this.spread);", + " const v = Math.random();", + " const rand = THREE.MathUtils.lerp(1 - this.thickness, 1, Math.random());", + " const theta = u * this.arc;", + " const phi = Math.acos(v);", + " const sinTheta = Math.sin(theta);", + " const cosTheta = Math.cos(theta);", + " const sinPhi = Math.sin(phi);", + " const cosPhi = Math.cos(phi);", + " p.position.x = sinPhi * cosTheta;", + " p.position.y = sinPhi * sinTheta;", + " p.position.z = cosPhi;", + " p.velocity.copy(p.position).multiplyScalar(p.startSpeed);", + " p.position.multiplyScalar(this.radius * rand);", + " }", + " toJSON() {", + " return {", + " type: 'hemisphere',", + " radius: this.radius,", + " arc: this.arc,", + " thickness: this.thickness,", + " mode: this.mode,", + " spread: this.spread,", + " speed: this.speed.toJSON(),", + " };", + " }", + " static fromJSON(json) {", + " return new HemisphereEmitter({", + " radius: json.radius,", + " arc: json.arc,", + " thickness: json.thickness,", + " mode: json.mode,", + " speed: json.speed ? ValueGeneratorFromJSON(json.speed) : undefined,", + " spread: json.spread,", + " });", + " }", + " clone() {", + " return new HemisphereEmitter({", + " radius: this.radius,", + " arc: this.arc,", + " thickness: this.thickness,", + " mode: this.mode,", + " speed: this.speed.clone(),", + " spread: this.spread,", + " });", + " }", + "}", + "", + "class GridEmitter {", + " constructor(parameters = {}) {", + " var _a, _b, _c, _d;", + " this.type = 'grid';", + " this.width = (_a = parameters.width) !== null && _a !== void 0 ? _a : 1;", + " this.height = (_b = parameters.height) !== null && _b !== void 0 ? _b : 1;", + " this.column = (_c = parameters.column) !== null && _c !== void 0 ? _c : 10;", + " this.row = (_d = parameters.row) !== null && _d !== void 0 ? _d : 10;", + " }", + " initialize(p) {", + " const r = Math.floor(Math.random() * this.row);", + " const c = Math.floor(Math.random() * this.column);", + " p.position.x = (c * this.width) / this.column - this.width / 2;", + " p.position.y = (r * this.height) / this.row - this.height / 2;", + " p.position.z = 0;", + " p.velocity.set(0, 0, p.startSpeed);", + " }", + " toJSON() {", + " return {", + " type: 'grid',", + " width: this.width,", + " height: this.height,", + " column: this.column,", + " row: this.row,", + " };", + " }", + " static fromJSON(json) {", + " return new GridEmitter(json);", + " }", + " clone() {", + " return new GridEmitter({", + " width: this.width,", + " height: this.height,", + " column: this.column,", + " row: this.row,", + " });", + " }", + " update(system, delta) { }", + "}", + "", + "class MeshSurfaceEmitter {", + " get geometry() {", + " return this._geometry;", + " }", + " set geometry(geometry) {", + " this._geometry = geometry;", + " if (geometry === undefined) {", + " return;", + " }", + " if (typeof geometry === 'string') {", + " return;", + " }", + " const tri = new THREE.Triangle();", + " this._triangleIndexToArea.length = 0;", + " let area = 0;", + " if (!geometry.getIndex()) {", + " return;", + " }", + " const array = geometry.getIndex().array;", + " const triCount = array.length / 3;", + " this._triangleIndexToArea.push(0);", + " for (let i = 0; i < triCount; i++) {", + " tri.setFromAttributeAndIndices(geometry.getAttribute('position'), array[i * 3], array[i * 3 + 1], array[i * 3 + 2]);", + " area += tri.getArea();", + " this._triangleIndexToArea.push(area);", + " }", + " geometry.userData.triangleIndexToArea = this._triangleIndexToArea;", + " }", + " constructor(geometry) {", + " this.type = 'mesh_surface';", + " this._triangleIndexToArea = [];", + " this._tempA = new THREE.Vector3();", + " this._tempB = new THREE.Vector3();", + " this._tempC = new THREE.Vector3();", + " if (!geometry) {", + " return;", + " }", + " this.geometry = geometry;", + " }", + " initialize(p) {", + " const geometry = this._geometry;", + " if (!geometry || geometry.getIndex() === null) {", + " p.position.set(0, 0, 0);", + " p.velocity.set(0, 0, 1).multiplyScalar(p.startSpeed);", + " return;", + " }", + " const triCount = this._triangleIndexToArea.length - 1;", + " let left = 0, right = triCount;", + " const target = Math.random() * this._triangleIndexToArea[triCount];", + " while (left + 1 < right) {", + " const mid = Math.floor((left + right) / 2);", + " if (target < this._triangleIndexToArea[mid]) {", + " right = mid;", + " }", + " else {", + " left = mid;", + " }", + " }", + " let u1 = Math.random();", + " let u2 = Math.random();", + " if (u1 + u2 > 1) {", + " u1 = 1 - u1;", + " u2 = 1 - u2;", + " }", + " const index1 = geometry.getIndex().array[left * 3];", + " const index2 = geometry.getIndex().array[left * 3 + 1];", + " const index3 = geometry.getIndex().array[left * 3 + 2];", + " const positionBuffer = geometry.getAttribute('position');", + " this._tempA.fromBufferAttribute(positionBuffer, index1);", + " this._tempB.fromBufferAttribute(positionBuffer, index2);", + " this._tempC.fromBufferAttribute(positionBuffer, index3);", + " this._tempB.sub(this._tempA);", + " this._tempC.sub(this._tempA);", + " this._tempA.addScaledVector(this._tempB, u1).addScaledVector(this._tempC, u2);", + " p.position.copy(this._tempA);", + " this._tempA.copy(this._tempB).cross(this._tempC).normalize();", + " p.velocity.copy(this._tempA).normalize().multiplyScalar(p.startSpeed);", + " }", + " toJSON() {", + " return {", + " type: 'mesh_surface',", + " mesh: this._geometry ? this._geometry.uuid : '',", + " };", + " }", + " static fromJSON(json, meta) {", + " return new MeshSurfaceEmitter(meta.geometries[json.geometry]);", + " }", + " clone() {", + " return new MeshSurfaceEmitter(this._geometry);", + " }", + " update(system, delta) { }", + "}", + "", + "const EmitterShapes = {", + " circle: {", + " type: 'circle',", + " params: [", + " ['radius', 'number'],", + " ['arc', 'radian'],", + " ['thickness', 'number'],", + " ['mode', 'emitterMode'],", + " ['spread', 'number'],", + " ['speed', 'valueFunc'],", + " ],", + " constructor: CircleEmitter,", + " loadJSON: CircleEmitter.fromJSON,", + " },", + " cone: {", + " type: 'cone',", + " params: [", + " ['radius', 'number'],", + " ['arc', 'radian'],", + " ['thickness', 'number'],", + " ['angle', 'radian'],", + " ['mode', 'emitterMode'],", + " ['spread', 'number'],", + " ['speed', 'valueFunc'],", + " ],", + " constructor: ConeEmitter,", + " loadJSON: ConeEmitter.fromJSON,", + " },", + " donut: {", + " type: 'donut',", + " params: [", + " ['radius', 'number'],", + " ['arc', 'radian'],", + " ['thickness', 'number'],", + " ['donutRadius', 'number'],", + " ['mode', 'emitterMode'],", + " ['spread', 'number'],", + " ['speed', 'valueFunc'],", + " ],", + " constructor: DonutEmitter,", + " loadJSON: DonutEmitter.fromJSON,", + " },", + " point: { type: 'point', params: [], constructor: PointEmitter, loadJSON: PointEmitter.fromJSON },", + " sphere: {", + " type: 'sphere',", + " params: [", + " ['radius', 'number'],", + " ['arc', 'radian'],", + " ['thickness', 'number'],", + " ['angle', 'radian'],", + " ['mode', 'emitterMode'],", + " ['spread', 'number'],", + " ['speed', 'valueFunc'],", + " ],", + " constructor: SphereEmitter,", + " loadJSON: SphereEmitter.fromJSON,", + " },", + " hemisphere: {", + " type: 'hemisphere',", + " params: [", + " ['radius', 'number'],", + " ['arc', 'radian'],", + " ['thickness', 'number'],", + " ['angle', 'radian'],", + " ['mode', 'emitterMode'],", + " ['spread', 'number'],", + " ['speed', 'valueFunc'],", + " ],", + " constructor: HemisphereEmitter,", + " loadJSON: HemisphereEmitter.fromJSON,", + " },", + " grid: {", + " type: 'grid',", + " params: [", + " ['width', 'number'],", + " ['height', 'number'],", + " ['rows', 'number'],", + " ['column', 'number'],", + " ],", + " constructor: GridEmitter,", + " loadJSON: GridEmitter.fromJSON,", + " },", + " mesh_surface: {", + " type: 'mesh_surface',", + " params: [['geometry', 'geometry']],", + " constructor: MeshSurfaceEmitter,", + " loadJSON: MeshSurfaceEmitter.fromJSON,", + " },", + "};", + "function EmitterFromJSON(json, meta) {", + " return EmitterShapes[json.type].loadJSON(json, meta);", + "}", + "", + "let RenderMode = void 0;", + "(function (RenderMode) {", + " RenderMode[RenderMode[\"BillBoard\"] = 0] = \"BillBoard\";", + " RenderMode[RenderMode[\"StretchedBillBoard\"] = 1] = \"StretchedBillBoard\";", + " RenderMode[RenderMode[\"Mesh\"] = 2] = \"Mesh\";", + " RenderMode[RenderMode[\"Trail\"] = 3] = \"Trail\";", + " RenderMode[RenderMode[\"HorizontalBillBoard\"] = 4] = \"HorizontalBillBoard\";", + " RenderMode[RenderMode[\"VerticalBillBoard\"] = 5] = \"VerticalBillBoard\";", + "})(RenderMode || (RenderMode = {}));", + "class VFXBatch extends THREE.Mesh {", + " constructor(settings) {", + " super();", + " this.type = 'VFXBatch';", + " this.maxParticles = 1000;", + " this.systems = new Set();", + " const layers = new THREE.Layers();", + " layers.mask = settings.layers.mask;", + " const newMat = settings.material.clone();", + " newMat.defines = {};", + " Object.assign(newMat.defines, settings.material.defines);", + " this.settings = {", + " instancingGeometry: settings.instancingGeometry,", + " renderMode: settings.renderMode,", + " renderOrder: settings.renderOrder,", + " material: newMat,", + " uTileCount: settings.uTileCount,", + " vTileCount: settings.vTileCount,", + " layers: layers,", + " };", + " this.frustumCulled = false;", + " this.renderOrder = this.settings.renderOrder;", + " }", + " addSystem(system) {", + " this.systems.add(system);", + " }", + " removeSystem(system) {", + " this.systems.delete(system);", + " }", + "}", + "", + "const UP = new THREE.Vector3(0, 0, 1);", + "const tempQ$1 = new THREE.Quaternion();", + "const tempV$1 = new THREE.Vector3();", + "const tempV2$1 = new THREE.Vector3();", + "new THREE.Vector3();", + "const PREWARM_FPS$1 = 60;", + "const DEFAULT_GEOMETRY$1 = new THREE.PlaneGeometry(1, 1, 1, 1);", + "class ParticleSystem {", + " set time(time) {", + " this.emissionState.time = time;", + " }", + " get time() {", + " return this.emissionState.time;", + " }", + " get layers() {", + " return this.rendererSettings.layers;", + " }", + " get texture() {", + " return this.rendererSettings.material.map;", + " }", + " set texture(texture) {", + " this.rendererSettings.material.map = texture;", + " this.neededToUpdateRender = true;", + " }", + " get material() {", + " return this.rendererSettings.material;", + " }", + " set material(material) {", + " this.rendererSettings.material = material;", + " this.neededToUpdateRender = true;", + " }", + " get uTileCount() {", + " return this.rendererSettings.uTileCount;", + " }", + " set uTileCount(u) {", + " this.rendererSettings.uTileCount = u;", + " this.neededToUpdateRender = true;", + " }", + " get vTileCount() {", + " return this.rendererSettings.vTileCount;", + " }", + " set vTileCount(v) {", + " this.rendererSettings.vTileCount = v;", + " this.neededToUpdateRender = true;", + " }", + " get instancingGeometry() {", + " return this.rendererSettings.instancingGeometry;", + " }", + " set instancingGeometry(geometry) {", + " this.restart();", + " this.particles.length = 0;", + " this.rendererSettings.instancingGeometry = geometry;", + " this.neededToUpdateRender = true;", + " }", + " get renderMode() {", + " return this.rendererSettings.renderMode;", + " }", + " set renderMode(renderMode) {", + " if ((this.rendererSettings.renderMode != RenderMode.Trail && renderMode === RenderMode.Trail) ||", + " (this.rendererSettings.renderMode == RenderMode.Trail && renderMode !== RenderMode.Trail)) {", + " this.restart();", + " this.particles.length = 0;", + " }", + " if (this.rendererSettings.renderMode !== renderMode) {", + " switch (renderMode) {", + " case RenderMode.Trail:", + " this.rendererEmitterSettings = {", + " startLength: new ConstantValue(30),", + " followLocalOrigin: false,", + " };", + " break;", + " case RenderMode.Mesh:", + " this.rendererEmitterSettings = {", + " geometry: new THREE.PlaneGeometry(1, 1),", + " };", + " this.startRotation = new AxisAngleGenerator(new THREE.Vector3(0, 1, 0), new ConstantValue(0));", + " break;", + " case RenderMode.StretchedBillBoard:", + " this.rendererEmitterSettings = { speedFactor: 0, lengthFactor: 2 };", + " if (this.rendererSettings.renderMode === RenderMode.Mesh) {", + " this.startRotation = new ConstantValue(0);", + " }", + " break;", + " case RenderMode.BillBoard:", + " case RenderMode.VerticalBillBoard:", + " case RenderMode.HorizontalBillBoard:", + " this.rendererEmitterSettings = {};", + " if (this.rendererSettings.renderMode === RenderMode.Mesh) {", + " this.startRotation = new ConstantValue(0);", + " }", + " break;", + " }", + " }", + " this.rendererSettings.renderMode = renderMode;", + " this.neededToUpdateRender = true;", + " }", + " get renderOrder() {", + " return this.rendererSettings.renderOrder;", + " }", + " set renderOrder(renderOrder) {", + " this.rendererSettings.renderOrder = renderOrder;", + " this.neededToUpdateRender = true;", + " }", + " get blending() {", + " return this.rendererSettings.material.blending;", + " }", + " set blending(blending) {", + " this.rendererSettings.material.blending = blending;", + " this.neededToUpdateRender = true;", + " }", + " constructor(parameters) {", + " var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m, _o, _p, _q, _r, _s, _t, _u, _v, _w, _x;", + " this.temp = new THREE.Vector3();", + " this.travelDistance = 0;", + " this.normalMatrix = new THREE.Matrix3();", + " this.firstTimeUpdate = true;", + " this.autoDestroy = parameters.autoDestroy === undefined ? false : parameters.autoDestroy;", + " this.duration = (_a = parameters.duration) !== null && _a !== void 0 ? _a : 1;", + " this.looping = parameters.looping === undefined ? true : parameters.looping;", + " this.prewarm = parameters.prewarm === undefined ? false : parameters.prewarm;", + " this.startLife = (_b = parameters.startLife) !== null && _b !== void 0 ? _b : new ConstantValue(5);", + " this.startSpeed = (_c = parameters.startSpeed) !== null && _c !== void 0 ? _c : new ConstantValue(0);", + " this.startRotation = (_d = parameters.startRotation) !== null && _d !== void 0 ? _d : new ConstantValue(0);", + " this.startSize = (_e = parameters.startSize) !== null && _e !== void 0 ? _e : new ConstantValue(1);", + " this.startColor = (_f = parameters.startColor) !== null && _f !== void 0 ? _f : new ConstantColor(new THREE.Vector4(1, 1, 1, 1));", + " this.emissionOverTime = (_g = parameters.emissionOverTime) !== null && _g !== void 0 ? _g : new ConstantValue(10);", + " this.emissionOverDistance = (_h = parameters.emissionOverDistance) !== null && _h !== void 0 ? _h : new ConstantValue(0);", + " this.emissionBursts = (_j = parameters.emissionBursts) !== null && _j !== void 0 ? _j : [];", + " this.onlyUsedByOther = (_k = parameters.onlyUsedByOther) !== null && _k !== void 0 ? _k : false;", + " this.emitterShape = (_l = parameters.shape) !== null && _l !== void 0 ? _l : new SphereEmitter();", + " this.behaviors = (_m = parameters.behaviors) !== null && _m !== void 0 ? _m : new Array();", + " this.worldSpace = (_o = parameters.worldSpace) !== null && _o !== void 0 ? _o : false;", + " this.rendererEmitterSettings = (_p = parameters.rendererEmitterSettings) !== null && _p !== void 0 ? _p : {};", + " if (parameters.renderMode === RenderMode.StretchedBillBoard) {", + " const stretchedBillboardSettings = this.rendererEmitterSettings;", + " if (parameters.speedFactor !== undefined) {", + " stretchedBillboardSettings.speedFactor = parameters.speedFactor;", + " }", + " stretchedBillboardSettings.speedFactor = (_q = stretchedBillboardSettings.speedFactor) !== null && _q !== void 0 ? _q : 0;", + " stretchedBillboardSettings.lengthFactor = (_r = stretchedBillboardSettings.lengthFactor) !== null && _r !== void 0 ? _r : 0;", + " }", + " this.rendererSettings = {", + " instancingGeometry: (_s = parameters.instancingGeometry) !== null && _s !== void 0 ? _s : DEFAULT_GEOMETRY$1,", + " renderMode: (_t = parameters.renderMode) !== null && _t !== void 0 ? _t : RenderMode.BillBoard,", + " renderOrder: (_u = parameters.renderOrder) !== null && _u !== void 0 ? _u : 0,", + " material: parameters.material,", + " uTileCount: (_v = parameters.uTileCount) !== null && _v !== void 0 ? _v : 1,", + " vTileCount: (_w = parameters.vTileCount) !== null && _w !== void 0 ? _w : 1,", + " layers: (_x = parameters.layers) !== null && _x !== void 0 ? _x : new THREE.Layers(),", + " };", + " this.neededToUpdateRender = true;", + " this.particles = new Array();", + " this.startTileIndex = parameters.startTileIndex || new ConstantValue(0);", + " this.emitter = new ParticleEmitter(this);", + " this.paused = false;", + " this.particleNum = 0;", + " this.emissionState = {", + " burstIndex: 0,", + " burstWaveIndex: 0,", + " time: 0,", + " waitEmiting: 0,", + " travelDistance: 0,", + " };", + " this.emitEnded = false;", + " this.markForDestroy = false;", + " this.prewarmed = false;", + " }", + " pause() {", + " this.paused = true;", + " }", + " play() {", + " this.paused = false;", + " }", + " spawn(count, emissionState, matrix) {", + " tempQ$1.setFromRotationMatrix(matrix);", + " const translation = tempV$1;", + " const quaternion = tempQ$1;", + " const scale = tempV2$1;", + " matrix.decompose(translation, quaternion, scale);", + " for (let i = 0; i < count; i++) {", + " this.particleNum++;", + " while (this.particles.length < this.particleNum) {", + " if (this.rendererSettings.renderMode === RenderMode.Trail) {", + " this.particles.push(new TrailParticle());", + " }", + " else {", + " this.particles.push(new SpriteParticle());", + " }", + " }", + " const particle = this.particles[this.particleNum - 1];", + " particle.speedModifier = 1;", + " this.startColor.genColor(particle.startColor, this.emissionState.time, {});", + " particle.color.copy(particle.startColor);", + " particle.startSpeed = this.startSpeed.genValue(emissionState.time / this.duration);", + " particle.life = this.startLife.genValue(emissionState.time / this.duration);", + " particle.age = 0;", + " particle.startSize = this.startSize.genValue(emissionState.time / this.duration);", + " particle.uvTile = Math.floor(this.startTileIndex.genValue() + 0.001);", + " particle.size = particle.startSize;", + " if (this.rendererSettings.renderMode === RenderMode.Mesh ||", + " this.rendererSettings.renderMode === RenderMode.BillBoard ||", + " this.rendererSettings.renderMode === RenderMode.VerticalBillBoard ||", + " this.rendererSettings.renderMode === RenderMode.HorizontalBillBoard ||", + " this.rendererSettings.renderMode === RenderMode.StretchedBillBoard) {", + " const sprite = particle;", + " if (this.rendererSettings.renderMode === RenderMode.Mesh) {", + " if (!(sprite.rotation instanceof THREE.Quaternion)) {", + " sprite.rotation = new THREE.Quaternion();", + " }", + " if (this.startRotation.type === 'rotation') {", + " this.startRotation.genValue(sprite.rotation, emissionState.time / this.duration);", + " }", + " else {", + " sprite.rotation.setFromAxisAngle(UP, this.startRotation.genValue((emissionState.time / this.duration)));", + " }", + " }", + " else {", + " if (this.startRotation.type === 'rotation') {", + " sprite.rotation = 0;", + " }", + " else {", + " sprite.rotation = this.startRotation.genValue(emissionState.time / this.duration);", + " }", + " }", + " }", + " else if (this.rendererSettings.renderMode === RenderMode.Trail) {", + " const trail = particle;", + " trail.length = this.rendererEmitterSettings.startLength.genValue(emissionState.time / this.duration);", + " }", + " this.emitterShape.initialize(particle);", + " if (this.rendererSettings.renderMode === RenderMode.Trail &&", + " this.rendererEmitterSettings.followLocalOrigin) {", + " const trail = particle;", + " trail.localPosition = new THREE.Vector3().copy(trail.position);", + " }", + " if (this.worldSpace) {", + " particle.position.applyMatrix4(matrix);", + " particle.startSize =", + " (particle.startSize * (Math.abs(scale.x) + Math.abs(scale.y) + Math.abs(scale.z))) / 3;", + " particle.size = particle.startSize;", + " particle.velocity.multiply(scale).applyMatrix3(this.normalMatrix);", + " if (particle.rotation && particle.rotation instanceof THREE.Quaternion) {", + " particle.rotation.multiplyQuaternions(tempQ$1, particle.rotation);", + " }", + " }", + " else {", + " if (this.onlyUsedByOther) {", + " particle.parentMatrix = matrix;", + " }", + " }", + " for (let j = 0; j < this.behaviors.length; j++) {", + " this.behaviors[j].initialize(particle);", + " }", + " }", + " }", + " endEmit() {", + " this.emitEnded = true;", + " if (this.autoDestroy) {", + " this.markForDestroy = true;", + " }", + " }", + " dispose() {", + " if (this._renderer)", + " this._renderer.deleteSystem(this);", + " this.emitter.dispose();", + " if (this.emitter.parent)", + " this.emitter.parent.remove(this.emitter);", + " }", + " restart() {", + " this.paused = false;", + " this.particleNum = 0;", + " this.emissionState.burstIndex = 0;", + " this.emissionState.burstWaveIndex = 0;", + " this.emissionState.time = 0;", + " this.emissionState.waitEmiting = 0;", + " this.behaviors.forEach((behavior) => {", + " behavior.reset();", + " });", + " this.emitEnded = false;", + " this.markForDestroy = false;", + " this.prewarmed = false;", + " }", + " update(delta) {", + " if (this.paused)", + " return;", + " let currentParent = this.emitter;", + " while (currentParent.parent) {", + " currentParent = currentParent.parent;", + " }", + " if (currentParent.type !== 'Scene') {", + " this.dispose();", + " return;", + " }", + " if (this.firstTimeUpdate) {", + " this.firstTimeUpdate = false;", + " this.emitter.updateWorldMatrix(true, false);", + " }", + " if (this.emitEnded && this.particleNum === 0) {", + " if (this.markForDestroy && this.emitter.parent)", + " this.dispose();", + " return;", + " }", + " if (this.looping && this.prewarm && !this.prewarmed) {", + " this.prewarmed = true;", + " for (let i = 0; i < this.duration * PREWARM_FPS$1; i++) {", + " this.update(1.0 / PREWARM_FPS$1);", + " }", + " }", + " if (delta > 0.1) {", + " delta = 0.1;", + " }", + " if (this.neededToUpdateRender) {", + " if (this._renderer)", + " this._renderer.updateSystem(this);", + " this.neededToUpdateRender = false;", + " }", + " if (!this.onlyUsedByOther) {", + " this.emit(delta, this.emissionState, this.emitter.matrixWorld);", + " }", + " this.emitterShape.update(this, delta);", + " for (let j = 0; j < this.behaviors.length; j++) {", + " for (let i = 0; i < this.particleNum; i++) {", + " if (!this.particles[i].died) {", + " this.behaviors[j].update(this.particles[i], delta);", + " }", + " }", + " this.behaviors[j].frameUpdate(delta);", + " }", + " for (let i = 0; i < this.particleNum; i++) {", + " if (this.rendererEmitterSettings.followLocalOrigin &&", + " this.particles[i].localPosition) {", + " this.particles[i].position.copy(this.particles[i].localPosition);", + " if (this.particles[i].parentMatrix) {", + " this.particles[i].position.applyMatrix4(this.particles[i].parentMatrix);", + " }", + " else {", + " this.particles[i].position.applyMatrix4(this.emitter.matrixWorld);", + " }", + " }", + " else {", + " this.particles[i].position.addScaledVector(this.particles[i].velocity, delta * this.particles[i].speedModifier);", + " }", + " this.particles[i].age += delta;", + " }", + " if (this.rendererSettings.renderMode === RenderMode.Trail) {", + " for (let i = 0; i < this.particleNum; i++) {", + " const particle = this.particles[i];", + " particle.update();", + " }", + " }", + " for (let i = 0; i < this.particleNum; i++) {", + " const particle = this.particles[i];", + " if (particle.died && (!(particle instanceof TrailParticle) || particle.previous.length === 0)) {", + " this.particles[i] = this.particles[this.particleNum - 1];", + " this.particles[this.particleNum - 1] = particle;", + " this.particleNum--;", + " i--;", + " }", + " }", + " }", + " emit(delta, emissionState, emitterMatrix) {", + " if (emissionState.time > this.duration) {", + " if (this.looping) {", + " emissionState.time -= this.duration;", + " emissionState.burstIndex = 0;", + " this.behaviors.forEach((behavior) => {", + " behavior.reset();", + " });", + " }", + " else {", + " if (!this.emitEnded && !this.onlyUsedByOther) {", + " this.endEmit();", + " }", + " }", + " }", + " this.normalMatrix.getNormalMatrix(emitterMatrix);", + " const totalSpawn = Math.ceil(emissionState.waitEmiting);", + " this.spawn(totalSpawn, emissionState, emitterMatrix);", + " emissionState.waitEmiting -= totalSpawn;", + " while (emissionState.burstIndex < this.emissionBursts.length &&", + " this.emissionBursts[emissionState.burstIndex].time <= emissionState.time) {", + " if (Math.random() < this.emissionBursts[emissionState.burstIndex].probability) {", + " const count = this.emissionBursts[emissionState.burstIndex].count.genValue(this.time);", + " this.spawn(count, emissionState, emitterMatrix);", + " }", + " emissionState.burstIndex++;", + " }", + " if (!this.emitEnded) {", + " emissionState.waitEmiting += delta * this.emissionOverTime.genValue(emissionState.time / this.duration);", + " if (emissionState.previousWorldPos != undefined) {", + " this.temp.set(emitterMatrix.elements[12], emitterMatrix.elements[13], emitterMatrix.elements[14]);", + " emissionState.travelDistance += emissionState.previousWorldPos.distanceTo(this.temp);", + " const emitPerMeter = this.emissionOverDistance.genValue(emissionState.time / this.duration);", + " if (emissionState.travelDistance * emitPerMeter > 0) {", + " const count = Math.floor(emissionState.travelDistance * emitPerMeter);", + " emissionState.travelDistance -= count / emitPerMeter;", + " emissionState.waitEmiting += count;", + " }", + " }", + " }", + " if (emissionState.previousWorldPos === undefined)", + " emissionState.previousWorldPos = new THREE.Vector3();", + " emissionState.previousWorldPos.set(emitterMatrix.elements[12], emitterMatrix.elements[13], emitterMatrix.elements[14]);", + " emissionState.time += delta;", + " }", + " toJSON(meta, options = {}) {", + " const isRootObject = meta === undefined || typeof meta === 'string';", + " if (isRootObject) {", + " meta = {", + " geometries: {},", + " materials: {},", + " textures: {},", + " images: {},", + " shapes: {},", + " skeletons: {},", + " animations: {},", + " nodes: {},", + " };", + " }", + " meta.materials[this.rendererSettings.material.uuid] = this.rendererSettings.material.toJSON(meta);", + " if (options.useUrlForImage) {", + " if (this.texture.source !== undefined) {", + " const image = this.texture.source;", + " meta.images[image.uuid] = {", + " uuid: image.uuid,", + " url: this.texture.image.url,", + " };", + " }", + " }", + " let rendererSettingsJSON;", + " if (this.renderMode === RenderMode.Trail) {", + " rendererSettingsJSON = {", + " startLength: this.rendererEmitterSettings.startLength.toJSON(),", + " followLocalOrigin: this.rendererEmitterSettings.followLocalOrigin,", + " };", + " }", + " else if (this.renderMode === RenderMode.Mesh) {", + " rendererSettingsJSON = {};", + " }", + " else if (this.renderMode === RenderMode.StretchedBillBoard) {", + " rendererSettingsJSON = {", + " speedFactor: this.rendererEmitterSettings.speedFactor,", + " lengthFactor: this.rendererEmitterSettings.lengthFactor,", + " };", + " }", + " else {", + " rendererSettingsJSON = {};", + " }", + " const geometry = this.rendererSettings.instancingGeometry;", + " if (meta.geometries && !meta.geometries[geometry.uuid]) {", + " meta.geometries[geometry.uuid] = geometry.toJSON();", + " }", + " return {", + " version: '2.0',", + " autoDestroy: this.autoDestroy,", + " looping: this.looping,", + " prewarm: this.prewarm,", + " duration: this.duration,", + " shape: this.emitterShape.toJSON(),", + " startLife: this.startLife.toJSON(),", + " startSpeed: this.startSpeed.toJSON(),", + " startRotation: this.startRotation.toJSON(),", + " startSize: this.startSize.toJSON(),", + " startColor: this.startColor.toJSON(),", + " emissionOverTime: this.emissionOverTime.toJSON(),", + " emissionOverDistance: this.emissionOverDistance.toJSON(),", + " emissionBursts: this.emissionBursts.map((burst) => ({", + " time: burst.time,", + " count: burst.count.toJSON(),", + " probability: burst.probability,", + " interval: burst.interval,", + " cycle: burst.cycle,", + " })),", + " onlyUsedByOther: this.onlyUsedByOther,", + " instancingGeometry: this.rendererSettings.instancingGeometry.uuid,", + " renderOrder: this.renderOrder,", + " renderMode: this.renderMode,", + " rendererEmitterSettings: rendererSettingsJSON,", + " material: this.rendererSettings.material.uuid,", + " layers: this.layers.mask,", + " startTileIndex: this.startTileIndex.toJSON(),", + " uTileCount: this.uTileCount,", + " vTileCount: this.vTileCount,", + " behaviors: this.behaviors.map((behavior) => behavior.toJSON()),", + " worldSpace: this.worldSpace,", + " };", + " }", + " static fromJSON(json, meta, dependencies) {", + " var _a, _b;", + " const shape = EmitterFromJSON(json.shape, meta);", + " let rendererEmitterSettings;", + " if (json.renderMode === RenderMode.Trail) {", + " let trailSettings = json.rendererEmitterSettings;", + " rendererEmitterSettings = {", + " startLength: trailSettings.startLength != undefined", + " ? ValueGeneratorFromJSON(trailSettings.startLength)", + " : new ConstantValue(30),", + " followLocalOrigin: trailSettings.followLocalOrigin,", + " };", + " }", + " else if (json.renderMode === RenderMode.Mesh) {", + " rendererEmitterSettings = {};", + " }", + " else if (json.renderMode === RenderMode.StretchedBillBoard) {", + " rendererEmitterSettings = json.rendererEmitterSettings;", + " if (json.speedFactor != undefined) {", + " rendererEmitterSettings.speedFactor = json.speedFactor;", + " }", + " }", + " else {", + " rendererEmitterSettings = {};", + " }", + " const layers = new THREE.Layers();", + " if (json.layers) {", + " layers.mask = json.layers;", + " }", + " const ps = new ParticleSystem({", + " autoDestroy: json.autoDestroy,", + " looping: json.looping,", + " prewarm: json.prewarm,", + " duration: json.duration,", + " shape: shape,", + " startLife: ValueGeneratorFromJSON(json.startLife),", + " startSpeed: ValueGeneratorFromJSON(json.startSpeed),", + " startRotation: GeneratorFromJSON(json.startRotation),", + " startSize: ValueGeneratorFromJSON(json.startSize),", + " startColor: ColorGeneratorFromJSON(json.startColor),", + " emissionOverTime: ValueGeneratorFromJSON(json.emissionOverTime),", + " emissionOverDistance: ValueGeneratorFromJSON(json.emissionOverDistance),", + " emissionBursts: (_a = json.emissionBursts) === null || _a === void 0 ? void 0 : _a.map((burst) => ({", + " time: burst.time,", + " count: typeof burst.count === 'number'", + " ? new ConstantValue(burst.count)", + " : ValueGeneratorFromJSON(burst.count),", + " probability: burst.probability,", + " interval: burst.interval,", + " cycle: burst.cycle,", + " })),", + " onlyUsedByOther: json.onlyUsedByOther,", + " instancingGeometry: meta.geometries[json.instancingGeometry],", + " renderMode: json.renderMode,", + " rendererEmitterSettings: rendererEmitterSettings,", + " renderOrder: json.renderOrder,", + " layers: layers,", + " material: json.material", + " ? meta.materials[json.material]", + " : json.texture", + " ? new THREE.MeshBasicMaterial({", + " map: meta.textures[json.texture],", + " transparent: (_b = json.transparent) !== null && _b !== void 0 ? _b : true,", + " blending: json.blending,", + " side: THREE.DoubleSide,", + " })", + " : new THREE.MeshBasicMaterial({", + " color: 0xffffff,", + " transparent: true,", + " blending: THREE.AdditiveBlending,", + " side: THREE.DoubleSide,", + " }),", + " startTileIndex: typeof json.startTileIndex === 'number'", + " ? new ConstantValue(json.startTileIndex)", + " : ValueGeneratorFromJSON(json.startTileIndex),", + " uTileCount: json.uTileCount,", + " vTileCount: json.vTileCount,", + " behaviors: [],", + " worldSpace: json.worldSpace,", + " });", + " ps.behaviors = json.behaviors.map((behaviorJson) => {", + " const behavior = BehaviorFromJSON(behaviorJson, ps);", + " if (behavior.type === 'EmitSubParticleSystem') {", + " dependencies[behaviorJson.subParticleSystem] = behavior;", + " }", + " return behavior;", + " });", + " return ps;", + " }", + " addBehavior(behavior) {", + " this.behaviors.push(behavior);", + " }", + " getRendererSettings() {", + " return this.rendererSettings;", + " }", + " clone() {", + " const newEmissionBursts = [];", + " for (const emissionBurst of this.emissionBursts) {", + " const newEmissionBurst = {};", + " Object.assign(newEmissionBurst, emissionBurst);", + " newEmissionBursts.push(newEmissionBurst);", + " }", + " const newBehaviors = [];", + " for (const behavior of this.behaviors) {", + " newBehaviors.push(behavior.clone());", + " }", + " let rendererEmitterSettings;", + " if (this.renderMode === RenderMode.Trail) {", + " rendererEmitterSettings = {", + " startLength: this.rendererEmitterSettings.startLength.clone(),", + " followLocalOrigin: this.rendererEmitterSettings.followLocalOrigin,", + " };", + " }", + " else {", + " rendererEmitterSettings = {};", + " }", + " const layers = new THREE.Layers();", + " layers.mask = this.layers.mask;", + " return new ParticleSystem({", + " autoDestroy: this.autoDestroy,", + " looping: this.looping,", + " duration: this.duration,", + " shape: this.emitterShape.clone(),", + " startLife: this.startLife.clone(),", + " startSpeed: this.startSpeed.clone(),", + " startRotation: this.startRotation.clone(),", + " startSize: this.startSize.clone(),", + " startColor: this.startColor.clone(),", + " emissionOverTime: this.emissionOverTime.clone(),", + " emissionOverDistance: this.emissionOverDistance.clone(),", + " emissionBursts: newEmissionBursts,", + " onlyUsedByOther: this.onlyUsedByOther,", + " instancingGeometry: this.rendererSettings.instancingGeometry,", + " renderMode: this.renderMode,", + " renderOrder: this.renderOrder,", + " rendererEmitterSettings: rendererEmitterSettings,", + " material: this.rendererSettings.material,", + " startTileIndex: this.startTileIndex,", + " uTileCount: this.uTileCount,", + " vTileCount: this.vTileCount,", + " behaviors: newBehaviors,", + " worldSpace: this.worldSpace,", + " layers: layers,", + " });", + " }", + "}", + "", + "var particle_frag = `", + "", + "#include ", + "#include ", + "#include ", + "#include ", + "#include ", + "#include ", + "#include ", + "", + "void main() {", + "", + " #include ", + " ", + " vec3 outgoingLight = vec3( 0.0 );", + " vec4 diffuseColor = vColor;", + " ", + " #include ", + " ", + " #ifdef USE_MAP", + " diffuseColor *= texture2D( map, vMapUv);", + " #endif", + " ", + " #include ", + "", + " outgoingLight = diffuseColor.rgb;", + " ", + " #ifdef USE_COLOR_AS_ALPHA", + " gl_FragColor = vec4( outgoingLight, diffuseColor.r );", + " #else", + " gl_FragColor = vec4( outgoingLight, diffuseColor.a );", + " #endif", + " ", + " ", + " #include ", + "", + "}", + "`;", + "", + "var particle_physics_frag = `", + "#define STANDARD", + "#ifdef PHYSICAL", + "#define IOR", + "#define SPECULAR", + "#endif", + "uniform vec3 diffuse;", + "uniform vec3 emissive;", + "uniform float roughness;", + "uniform float metalness;", + "uniform float opacity;", + "#ifdef IOR", + "uniform float ior;", + "#endif", + "#ifdef SPECULAR", + "uniform float specularIntensity;", + "uniform vec3 specularColor;", + "#ifdef USE_SPECULARINTENSITYMAP", + "uniform sampler2D specularIntensityMap;", + "#endif", + "#ifdef USE_SPECULARCOLORMAP", + "uniform sampler2D specularColorMap;", + "#endif", + "#endif", + "#ifdef USE_CLEARCOAT", + "uniform float clearcoat;", + "uniform float clearcoatRoughness;", + "#endif", + "#ifdef USE_IRIDESCENCE", + "uniform float iridescence;", + "uniform float iridescenceIOR;", + "uniform float iridescenceThicknessMinimum;", + "uniform float iridescenceThicknessMaximum;", + "#endif", + "#ifdef USE_SHEEN", + "uniform vec3 sheenColor;", + "uniform float sheenRoughness;", + "#ifdef USE_SHEENCOLORMAP", + "uniform sampler2D sheenColorMap;", + "#endif", + "#ifdef USE_SHEENROUGHNESSMAP", + "uniform sampler2D sheenRoughnessMap;", + "#endif", + "#endif", + "", + "varying vec3 vViewPosition;", + "#include ", + "#include ", + "#include ", + "#include ", + "#include ", + "#include ", + "#include ", + "#include ", + "#include ", + "#include ", + "#include ", + "#include ", + "#include ", + "#include ", + "#include ", + "#include ", + "#include ", + "#include ", + "#include ", + "#include ", + "#include ", + "#include ", + "#include ", + "#include ", + "#include ", + "#include ", + "#include ", + "#include ", + "#include ", + "#include ", + "", + "void main() {", + " #include ", + " vec4 diffuseColor = vec4( diffuse, opacity );", + " ReflectedLight reflectedLight = ReflectedLight( vec3( 0.0 ), vec3( 0.0 ), vec3( 0.0 ), vec3( 0.0 ) );", + " vec3 totalEmissiveRadiance = emissive;", + " #include ", + " #include ", + " #include ", + " #include ", + " #include ", + " #include ", + " #include ", + " #include ", + " #include ", + " #include ", + " #include ", + " #include ", + " // accumulation", + " #include ", + " #include ", + " #include ", + " #include ", + " // modulation", + " #include ", + " vec3 totalDiffuse = reflectedLight.directDiffuse + reflectedLight.indirectDiffuse;", + " vec3 totalSpecular = reflectedLight.directSpecular + reflectedLight.indirectSpecular;", + " #include ", + " vec3 outgoingLight = totalDiffuse + totalSpecular + totalEmissiveRadiance;", + " #ifdef USE_SHEEN", + " // Sheen energy compensation approximation calculation can be found at the end of", + " // https://drive.google.com/file/d/1T0D1VSyR4AllqIJTQAraEIzjlb5h4FKH/view?usp=sharing", + " float sheenEnergyComp = 1.0 - 0.157 * max3( material.sheenColor );", + " outgoingLight = outgoingLight * sheenEnergyComp + sheenSpecular;", + " #endif", + " #ifdef USE_CLEARCOAT", + " float dotNVcc = saturate( dot( geometry.clearcoatNormal, geometry.viewDir ) );", + " vec3 Fcc = F_Schlick( material.clearcoatF0, material.clearcoatF90, dotNVcc );", + " outgoingLight = outgoingLight * ( 1.0 - material.clearcoat * Fcc ) + clearcoatSpecular * material.clearcoat;", + " #endif", + " #include ", + " #include ", + " #include ", + " #include ", + " #include ", + " #include ", + "}`;", + "", + "var uv_vertex_tile = `", + "", + " #ifdef UV_TILE", + " float col = mod(uvTile, tileCount.x);", + " float row = (tileCount.y - floor(uvTile / tileCount.x) - 1.0);", + " ", + " mat3 tileTransform = mat3(", + " 1.0 / tileCount.x, 0.0, 0.0,", + " 0.0, 1.0 / tileCount.y, 0.0, ", + " col / tileCount.x, row / tileCount.y, 1.0);", + " #else", + " mat3 tileTransform = mat3(1.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 1.0);", + " #endif", + "", + "#if defined( USE_UV ) || defined( USE_ANISOTROPY )", + "", + "vUv = (tileTransform *vec3( uv, 1 )).xy;", + "", + "#endif", + "#ifdef USE_MAP", + "", + "vMapUv = ( tileTransform * (mapTransform * vec3( MAP_UV, 1 ) )).xy;", + "", + "#endif", + "#ifdef USE_ALPHAMAP", + "", + "vAlphaMapUv = ( tileTransform * (alphaMapTransform * vec3( ALPHAMAP_UV, 1 ) )).xy;", + "", + "#endif", + "#ifdef USE_LIGHTMAP", + "", + "vLightMapUv = ( tileTransform * (lightMapTransform * vec3( LIGHTMAP_UV, 1 ) )).xy;", + "", + "#endif", + "#ifdef USE_AOMAP", + "", + "vAoMapUv = ( tileTransform * (aoMapTransform * vec3( AOMAP_UV, 1 ) )).xy;", + "", + "#endif", + "#ifdef USE_BUMPMAP", + "", + "vBumpMapUv = ( tileTransform * (bumpMapTransform * vec3( BUMPMAP_UV, 1 ) )).xy;", + "", + "#endif", + "#ifdef USE_NORMALMAP", + "", + "vNormalMapUv = ( tileTransform * (normalMapTransform * vec3( NORMALMAP_UV, 1 ) )).xy;", + "", + "#endif", + "#ifdef USE_DISPLACEMENTMAP", + "", + "vDisplacementMapUv = ( tileTransform * (displacementMapTransform * vec3( DISPLACEMENTMAP_UV, 1 ) )).xy;", + "", + "#endif", + "#ifdef USE_EMISSIVEMAP", + "", + "vEmissiveMapUv = ( tileTransform * (emissiveMapTransform * vec3( EMISSIVEMAP_UV, 1 ) )).xy;", + "", + "#endif", + "#ifdef USE_METALNESSMAP", + "", + "vMetalnessMapUv = ( tileTransform * (metalnessMapTransform * vec3( METALNESSMAP_UV, 1 ) )).xy;", + "", + "#endif", + "#ifdef USE_ROUGHNESSMAP", + "", + "vRoughnessMapUv = ( tileTransform * (roughnessMapTransform * vec3( ROUGHNESSMAP_UV, 1 ) )).xy;", + "", + "#endif", + "#ifdef USE_ANISOTROPYMAP", + "", + "vAnisotropyMapUv = ( tileTransform * (anisotropyMapTransform * vec3( ANISOTROPYMAP_UV, 1 ) )).xy;", + "", + "#endif", + "#ifdef USE_CLEARCOATMAP", + "", + "vClearcoatMapUv = ( tileTransform * (clearcoatMapTransform * vec3( CLEARCOATMAP_UV, 1 ) )).xy;", + "", + "#endif", + "#ifdef USE_CLEARCOAT_NORMALMAP", + "", + "vClearcoatNormalMapUv = ( tileTransform * (clearcoatNormalMapTransform * vec3( CLEARCOAT_NORMALMAP_UV, 1 ) )).xy;", + "", + "#endif", + "#ifdef USE_CLEARCOAT_ROUGHNESSMAP", + "", + "vClearcoatRoughnessMapUv = ( tileTransform * (clearcoatRoughnessMapTransform * vec3( CLEARCOAT_ROUGHNESSMAP_UV, 1 ) )).xy;", + "", + "#endif", + "#ifdef USE_IRIDESCENCEMAP", + "", + "vIridescenceMapUv = ( tileTransform * (iridescenceMapTransform * vec3( IRIDESCENCEMAP_UV, 1 ) )).xy;", + "", + "#endif", + "#ifdef USE_IRIDESCENCE_THICKNESSMAP", + "", + "vIridescenceThicknessMapUv = ( tileTransform * (iridescenceThicknessMapTransform * vec3( IRIDESCENCE_THICKNESSMAP_UV, 1 ) )).xy;", + "", + "#endif", + "#ifdef USE_SHEEN_COLORMAP", + "", + "vSheenColorMapUv = ( tileTransform * (sheenColorMapTransform * vec3( SHEEN_COLORMAP_UV, 1 ) )).xy;", + "", + "#endif", + "#ifdef USE_SHEEN_ROUGHNESSMAP", + "", + "vSheenRoughnessMapUv = ( tileTransform * (sheenRoughnessMapTransform * vec3( SHEEN_ROUGHNESSMAP_UV, 1 ) )).xy;", + "", + "#endif", + "#ifdef USE_SPECULARMAP", + "", + "vSpecularMapUv = ( tileTransform * (specularMapTransform * vec3( SPECULARMAP_UV, 1 ) )).xy;", + "", + "#endif", + "#ifdef USE_SPECULAR_COLORMAP", + "", + "vSpecularColorMapUv = ( tileTransform * (specularColorMapTransform * vec3( SPECULAR_COLORMAP_UV, 1 ) )).xy;", + "", + "#endif", + "#ifdef USE_SPECULAR_INTENSITYMAP", + "", + "vSpecularIntensityMapUv = ( tileTransform * (specularIntensityMapTransform * vec3( SPECULAR_INTENSITYMAP_UV, 1 ) )).xy;", + "", + "#endif", + "#ifdef USE_TRANSMISSIONMAP", + "", + "vTransmissionMapUv = ( tileTransform * transmissionMapTransform * vec3( TRANSMISSIONMAP_UV, 1 ) )).xy;", + "", + "#endif", + "#ifdef USE_THICKNESSMAP", + "", + "vThicknessMapUv = ( tileTransform * thicknessMapTransform * vec3( THICKNESSMAP_UV, 1 ) )).xy;", + "", + "#endif", + "`;", + "", + "var particle_vert = `", + "#include ", + "#include ", + "#include ", + "#include ", + "#include ", + "", + "attribute vec3 offset;", + "attribute float rotation;", + "attribute float size;", + "attribute float uvTile;", + "", + "#ifdef UV_TILE", + "uniform vec2 tileCount;", + "#endif", + "", + "void main() {", + "", + " ${uv_vertex_tile}", + "\t", + " vec2 alignedPosition = ( position.xy ) * size;", + " ", + " vec2 rotatedPosition;", + " rotatedPosition.x = cos( rotation ) * alignedPosition.x - sin( rotation ) * alignedPosition.y;", + " rotatedPosition.y = sin( rotation ) * alignedPosition.x + cos( rotation ) * alignedPosition.y;", + "#ifdef HORIZONTAL", + " vec4 mvPosition = modelMatrix * vec4( offset, 1.0 );", + " mvPosition.x += rotatedPosition.x;", + " mvPosition.z -= rotatedPosition.y;", + " mvPosition = viewMatrix * mvPosition;", + "#elif defined(VERTICAL)", + " vec4 mvPosition = modelMatrix * vec4( offset, 1.0 );", + " mvPosition.y += rotatedPosition.y;", + " mvPosition = viewMatrix * mvPosition;", + " mvPosition.x += rotatedPosition.x;", + "#else", + " vec4 mvPosition = modelViewMatrix * vec4( offset, 1.0 );", + " mvPosition.xy += rotatedPosition;", + "#endif", + "", + "\tvColor = color;", + "", + "\tgl_Position = projectionMatrix * mvPosition;", + "", + "\t#include ", + "\t#include ", + "", + "}", + "`;", + "", + "var local_particle_vert = `", + "#include ", + "#include ", + "#include ", + "#include ", + "#include ", + "", + "attribute vec3 offset;", + "attribute vec4 rotation;", + "attribute float size;", + "// attribute vec4 color;", + "attribute float uvTile;", + "", + "#ifdef UV_TILE", + "uniform vec2 tileCount;", + "#endif", + "", + "void main() {", + "", + " ${uv_vertex_tile}", + " ", + " float x2 = rotation.x + rotation.x, y2 = rotation.y + rotation.y, z2 = rotation.z + rotation.z;", + " float xx = rotation.x * x2, xy = rotation.x * y2, xz = rotation.x * z2;", + " float yy = rotation.y * y2, yz = rotation.y * z2, zz = rotation.z * z2;", + " float wx = rotation.w * x2, wy = rotation.w * y2, wz = rotation.w * z2;", + " float sx = size, sy = size, sz = size;", + " ", + " mat4 matrix = mat4(( 1.0 - ( yy + zz ) ) * sx, ( xy + wz ) * sx, ( xz - wy ) * sx, 0.0, // 1. column", + " ( xy - wz ) * sy, ( 1.0 - ( xx + zz ) ) * sy, ( yz + wx ) * sy, 0.0, // 2. column", + " ( xz + wy ) * sz, ( yz - wx ) * sz, ( 1.0 - ( xx + yy ) ) * sz, 0.0, // 3. column", + " offset.x, offset.y, offset.z, 1.0);", + " ", + " vec4 mvPosition = modelViewMatrix * (matrix * vec4( position, 1.0 ));", + "", + "\tvColor = color;", + "", + "\tgl_Position = projectionMatrix * mvPosition;", + "", + "\t#include ", + "\t#include ", + "", + "}", + "`;", + "", + "var local_particle_physics_vert = `", + "#define STANDARD", + "varying vec3 vViewPosition;", + "#ifdef USE_TRANSMISSION", + "\tvarying vec3 vWorldPosition;", + "#endif", + "#include ", + "#include ", + "", + "attribute vec3 offset;", + "attribute vec4 rotation;", + "attribute float size;", + "attribute float uvTile;", + "", + "#ifdef UV_TILE", + "uniform vec2 tileCount;", + "#endif", + "", + "#include ", + "#include ", + "#include ", + "#include ", + "#include ", + "#include ", + "#include ", + "#include ", + "#include ", + "", + "void main() {", + " ${uv_vertex_tile}", + "", + " float x2 = rotation.x + rotation.x, y2 = rotation.y + rotation.y, z2 = rotation.z + rotation.z;", + " float xx = rotation.x * x2, xy = rotation.x * y2, xz = rotation.x * z2;", + " float yy = rotation.y * y2, yz = rotation.y * z2, zz = rotation.z * z2;", + " float wx = rotation.w * x2, wy = rotation.w * y2, wz = rotation.w * z2;", + " float sx = size, sy = size, sz = size;", + "", + " mat4 particleMatrix = mat4(( 1.0 - ( yy + zz ) ) * sx, ( xy + wz ) * sx, ( xz - wy ) * sx, 0.0, // 1. column", + " ( xy - wz ) * sy, ( 1.0 - ( xx + zz ) ) * sy, ( yz + wx ) * sy, 0.0, // 2. column", + " ( xz + wy ) * sz, ( yz - wx ) * sz, ( 1.0 - ( xx + yy ) ) * sz, 0.0, // 3. column", + " offset.x, offset.y, offset.z, 1.0);", + "", + "\t#include ", + "\t#include ", + "\t#include ", + "\t#include ", + "\t#include ", + "\t#include ", + "", + "\t// replace defaultnormal_vertex", + "\tvec3 transformedNormal = objectNormal;", + " mat3 m = mat3( particleMatrix );", + " transformedNormal /= vec3( dot( m[ 0 ], m[ 0 ] ), dot( m[ 1 ], m[ 1 ] ), dot( m[ 2 ], m[ 2 ] ) );", + " transformedNormal = m * transformedNormal;", + " transformedNormal = normalMatrix * transformedNormal;", + " #ifdef FLIP_SIDED", + " transformedNormal = - transformedNormal;", + " #endif", + " #ifdef USE_TANGENT", + " vec3 transformedTangent = ( modelViewMatrix * vec4( objectTangent, 0.0 ) ).xyz;", + " #ifdef FLIP_SIDED", + " transformedTangent = - transformedTangent;", + " #endif", + " #endif", + "", + "\t#include ", + "\t#include ", + "\t#include ", + "\t#include ", + "\t#include ", + "", + "\t// replace include ", + " vec4 mvPosition = vec4( transformed, 1.0 );", + " mvPosition = modelViewMatrix * (particleMatrix * mvPosition);", + "\tgl_Position = projectionMatrix * mvPosition;", + "", + "\t#include ", + "\t#include ", + "\t", + "\tvViewPosition = - mvPosition.xyz;", + "\t", + "\t#include ", + "\t#include ", + "\t#include ", + "#ifdef USE_TRANSMISSION", + "\tvWorldPosition = worldPosition.xyz;", + "#endif", + "}", + "`;", + "", + "var stretched_bb_particle_vert = `", + "#include ", + "#include ", + "#include ", + "#include ", + "#include ", + "", + "attribute vec3 offset;", + "attribute float rotation;", + "attribute float size;", + "attribute vec4 velocity;", + "attribute float uvTile;", + "", + "#ifdef UV_TILE", + "uniform vec2 tileCount;", + "#endif", + "", + "uniform float speedFactor;", + "", + "void main() {", + "", + " ${uv_vertex_tile}", + " ", + " float lengthFactor = velocity.w;", + "#ifdef USE_SKEW", + " vec4 mvPosition = modelViewMatrix * vec4( offset, 1.0 );", + " vec3 viewVelocity = normalMatrix * velocity.xyz;", + "", + " vec3 scaledPos = vec3(position.xy * size, position.z);", + " float vlength = length(viewVelocity);", + " vec3 projVelocity = dot(scaledPos, viewVelocity) * viewVelocity / vlength;", + " mvPosition.xyz += scaledPos + projVelocity * (speedFactor / size + lengthFactor / vlength);", + "#else", + " vec4 mvPosition = modelViewMatrix * vec4( offset, 1.0 );", + " vec3 viewVelocity = normalMatrix * velocity.xyz;", + " float vlength = length(viewVelocity); ", + " mvPosition.xyz += position.y * normalize(cross(mvPosition.xyz, viewVelocity)) * size; // switch the cross to match unity implementation", + " mvPosition.xyz -= (position.x + 0.5) * viewVelocity * (1.0 + lengthFactor / vlength) * size; // minus position.x to match unity implementation", + "#endif", + "\tvColor = color;", + "\tgl_Position = projectionMatrix * mvPosition;", + "\t#include ", + "\t#include ", + "}", + "`;", + "", + "function getMaterialUVChannelName(value) {", + " if (value === 0)", + " return 'uv';", + " return `uv${value}`;", + "}", + "", + "new THREE.Vector3(0, 0, 1);", + "class SpriteBatch extends VFXBatch {", + " constructor(settings) {", + " super(settings);", + " this.vector_ = new THREE.Vector3();", + " this.vector2_ = new THREE.Vector3();", + " this.vector3_ = new THREE.Vector3();", + " this.quaternion_ = new THREE.Quaternion();", + " this.quaternion2_ = new THREE.Quaternion();", + " this.quaternion3_ = new THREE.Quaternion();", + " this.rotationMat_ = new THREE.Matrix3();", + " this.rotationMat2_ = new THREE.Matrix3();", + " this.maxParticles = 1000;", + " this.setupBuffers();", + " this.rebuildMaterial();", + " }", + " buildExpandableBuffers() {", + " this.offsetBuffer = new THREE.InstancedBufferAttribute(new Float32Array(this.maxParticles * 3), 3);", + " this.offsetBuffer.setUsage(THREE.DynamicDrawUsage);", + " this.geometry.setAttribute('offset', this.offsetBuffer);", + " this.colorBuffer = new THREE.InstancedBufferAttribute(new Float32Array(this.maxParticles * 4), 4);", + " this.colorBuffer.setUsage(THREE.DynamicDrawUsage);", + " this.geometry.setAttribute('color', this.colorBuffer);", + " if (this.settings.renderMode === RenderMode.Mesh) {", + " this.rotationBuffer = new THREE.InstancedBufferAttribute(new Float32Array(this.maxParticles * 4), 4);", + " this.rotationBuffer.setUsage(THREE.DynamicDrawUsage);", + " this.geometry.setAttribute('rotation', this.rotationBuffer);", + " }", + " else if (this.settings.renderMode === RenderMode.BillBoard ||", + " this.settings.renderMode === RenderMode.HorizontalBillBoard ||", + " this.settings.renderMode === RenderMode.VerticalBillBoard ||", + " this.settings.renderMode === RenderMode.StretchedBillBoard) {", + " this.rotationBuffer = new THREE.InstancedBufferAttribute(new Float32Array(this.maxParticles), 1);", + " this.rotationBuffer.setUsage(THREE.DynamicDrawUsage);", + " this.geometry.setAttribute('rotation', this.rotationBuffer);", + " }", + " this.sizeBuffer = new THREE.InstancedBufferAttribute(new Float32Array(this.maxParticles), 1);", + " this.sizeBuffer.setUsage(THREE.DynamicDrawUsage);", + " this.geometry.setAttribute('size', this.sizeBuffer);", + " this.uvTileBuffer = new THREE.InstancedBufferAttribute(new Float32Array(this.maxParticles), 1);", + " this.uvTileBuffer.setUsage(THREE.DynamicDrawUsage);", + " this.geometry.setAttribute('uvTile', this.uvTileBuffer);", + " if (this.settings.renderMode === RenderMode.StretchedBillBoard) {", + " this.velocityBuffer = new THREE.InstancedBufferAttribute(new Float32Array(this.maxParticles * 4), 4);", + " this.velocityBuffer.setUsage(THREE.DynamicDrawUsage);", + " this.geometry.setAttribute('velocity', this.velocityBuffer);", + " }", + " }", + " setupBuffers() {", + " if (this.geometry)", + " this.geometry.dispose();", + " this.geometry = new THREE.InstancedBufferGeometry();", + " this.geometry.setIndex(this.settings.instancingGeometry.getIndex());", + " if (this.settings.instancingGeometry.hasAttribute('normal')) {", + " this.geometry.setAttribute('normal', this.settings.instancingGeometry.getAttribute('normal'));", + " }", + " this.geometry.setAttribute('position', this.settings.instancingGeometry.getAttribute('position'));", + " this.geometry.setAttribute('uv', this.settings.instancingGeometry.getAttribute('uv'));", + " this.buildExpandableBuffers();", + " }", + " expandBuffers(target) {", + " while (target >= this.maxParticles) {", + " this.maxParticles *= 2;", + " }", + " this.setupBuffers();", + " }", + " rebuildMaterial() {", + " this.layers.mask = this.settings.layers.mask;", + " let uniforms;", + " const defines = {};", + " if (this.settings.material.type === 'MeshStandardMaterial' ||", + " this.settings.material.type === 'MeshPhysicalMaterial') {", + " const mat = this.settings.material;", + " uniforms = THREE.UniformsUtils.merge([", + " THREE.UniformsLib.common,", + " THREE.UniformsLib.envmap,", + " THREE.UniformsLib.aomap,", + " THREE.UniformsLib.lightmap,", + " THREE.UniformsLib.emissivemap,", + " THREE.UniformsLib.bumpmap,", + " THREE.UniformsLib.normalmap,", + " THREE.UniformsLib.displacementmap,", + " THREE.UniformsLib.roughnessmap,", + " THREE.UniformsLib.metalnessmap,", + " THREE.UniformsLib.fog,", + " THREE.UniformsLib.lights,", + " {", + " emissive: { value: new THREE.Color(0x000000) },", + " roughness: { value: 1.0 },", + " metalness: { value: 0.0 },", + " envMapIntensity: { value: 1 },", + " },", + " ]);", + " uniforms['diffuse'].value = mat.color;", + " uniforms['opacity'].value = mat.opacity;", + " uniforms['emissive'].value = mat.emissive;", + " uniforms['roughness'].value = mat.roughness;", + " uniforms['metalness'].value = mat.metalness;", + " if (mat.envMap) {", + " uniforms['envMap'].value = mat.envMap;", + " uniforms['envMapIntensity'].value = mat.envMapIntensity;", + " }", + " if (mat.normalMap) {", + " uniforms['normalMap'].value = mat.normalMap;", + " uniforms['normalScale'].value = mat.normalScale;", + " }", + " if (mat.roughnessMap) {", + " uniforms['roughnessMap'].value = mat.roughnessMap;", + " }", + " if (mat.metalnessMap) {", + " uniforms['metalnessMap'].value = mat.metalnessMap;", + " }", + " if (mat.map) {", + " uniforms['map'] = new THREE.Uniform(mat.map);", + " }", + " }", + " else {", + " uniforms = {};", + " uniforms['map'] = new THREE.Uniform(this.settings.material.map);", + " }", + " if (this.settings.material.alphaTest) {", + " defines['USE_ALPHATEST'] = '';", + " uniforms['alphaTest'] = new THREE.Uniform(this.settings.material.alphaTest);", + " }", + " defines['USE_UV'] = '';", + " const uTileCount = this.settings.uTileCount;", + " const vTileCount = this.settings.vTileCount;", + " if (uTileCount > 1 || vTileCount > 1) {", + " defines['UV_TILE'] = '';", + " uniforms['tileCount'] = new THREE.Uniform(new THREE.Vector2(uTileCount, vTileCount));", + " }", + " if (this.settings.material.defines &&", + " this.settings.material.defines['USE_COLOR_AS_ALPHA'] !== undefined) {", + " defines['USE_COLOR_AS_ALPHA'] = '';", + " }", + " if (this.settings.material.normalMap) {", + " defines['USE_NORMALMAP'] = '';", + " defines['NORMALMAP_UV'] = getMaterialUVChannelName(this.settings.material.normalMap.channel);", + " uniforms['normalMapTransform'] = new THREE.Uniform(new THREE.Matrix3().copy(this.settings.material.normalMap.matrix));", + " }", + " if (this.settings.material.map) {", + " defines['USE_MAP'] = '';", + " defines['MAP_UV'] = getMaterialUVChannelName(this.settings.material.map.channel);", + " uniforms['mapTransform'] = new THREE.Uniform(new THREE.Matrix3().copy(this.settings.material.map.matrix));", + " }", + " defines['USE_COLOR_ALPHA'] = '';", + " let needLights = false;", + " if (this.settings.renderMode === RenderMode.BillBoard ||", + " this.settings.renderMode === RenderMode.VerticalBillBoard ||", + " this.settings.renderMode === RenderMode.HorizontalBillBoard ||", + " this.settings.renderMode === RenderMode.Mesh) {", + " let vertexShader;", + " let fragmentShader;", + " if (this.settings.renderMode === RenderMode.Mesh) {", + " if (this.settings.material.type === 'MeshStandardMaterial' ||", + " this.settings.material.type === 'MeshPhysicalMaterial') {", + " defines['USE_COLOR'] = '';", + " vertexShader = local_particle_physics_vert;", + " fragmentShader = particle_physics_frag;", + " needLights = true;", + " }", + " else {", + " vertexShader = local_particle_vert;", + " fragmentShader = particle_frag;", + " }", + " }", + " else {", + " vertexShader = particle_vert;", + " fragmentShader = particle_frag;", + " }", + " if (this.settings.renderMode === RenderMode.VerticalBillBoard) {", + " defines['VERTICAL'] = '';", + " }", + " else if (this.settings.renderMode === RenderMode.HorizontalBillBoard) {", + " defines['HORIZONTAL'] = '';", + " }", + " this.material = new THREE.ShaderMaterial({", + " uniforms: uniforms,", + " defines: defines,", + " vertexShader: vertexShader,", + " fragmentShader: fragmentShader,", + " transparent: this.settings.material.transparent,", + " depthWrite: !this.settings.material.transparent,", + " blending: this.settings.material.blending,", + " side: this.settings.material.side,", + " alphaTest: this.settings.material.alphaTest,", + " lights: needLights,", + " });", + " }", + " else if (this.settings.renderMode === RenderMode.StretchedBillBoard) {", + " uniforms['speedFactor'] = new THREE.Uniform(1.0);", + " this.material = new THREE.ShaderMaterial({", + " uniforms: uniforms,", + " defines: defines,", + " vertexShader: stretched_bb_particle_vert,", + " fragmentShader: particle_frag,", + " transparent: this.settings.material.transparent,", + " depthWrite: !this.settings.material.transparent,", + " blending: this.settings.material.blending,", + " side: this.settings.material.side,", + " alphaTest: this.settings.material.alphaTest,", + " });", + " }", + " else {", + " throw new Error('render mode unavailable');", + " }", + " }", + " update() {", + " let index = 0;", + " let particleCount = 0;", + " this.systems.forEach((system) => {", + " particleCount += system.particleNum;", + " });", + " if (particleCount > this.maxParticles) {", + " this.expandBuffers(particleCount);", + " }", + " this.systems.forEach((system) => {", + " const particles = system.particles;", + " const particleNum = system.particleNum;", + " const rotation = this.quaternion2_;", + " const translation = this.vector2_;", + " const scale = this.vector3_;", + " system.emitter.matrixWorld.decompose(translation, rotation, scale);", + " this.rotationMat_.setFromMatrix4(system.emitter.matrixWorld);", + " for (let j = 0; j < particleNum; j++ , index++) {", + " const particle = particles[j];", + " if (this.settings.renderMode === RenderMode.Mesh) {", + " let q;", + " if (system.worldSpace) {", + " q = particle.rotation;", + " }", + " else {", + " let parentQ;", + " if (particle.parentMatrix) {", + " parentQ = this.quaternion3_.setFromRotationMatrix(particle.parentMatrix);", + " }", + " else {", + " parentQ = rotation;", + " }", + " q = this.quaternion_;", + " q.copy(particle.rotation).multiply(parentQ);", + " }", + " this.rotationBuffer.setXYZW(index, q.x, q.y, q.z, q.w);", + " }", + " else if (this.settings.renderMode === RenderMode.StretchedBillBoard ||", + " this.settings.renderMode === RenderMode.VerticalBillBoard ||", + " this.settings.renderMode === RenderMode.HorizontalBillBoard ||", + " this.settings.renderMode === RenderMode.BillBoard) {", + " this.rotationBuffer.setX(index, particle.rotation);", + " }", + " let vec;", + " if (system.worldSpace) {", + " vec = particle.position;", + " }", + " else {", + " vec = this.vector_;", + " if (particle.parentMatrix) {", + " vec.copy(particle.position).applyMatrix4(particle.parentMatrix);", + " }", + " else {", + " vec.copy(particle.position).applyMatrix4(system.emitter.matrixWorld);", + " }", + " }", + " this.offsetBuffer.setXYZ(index, vec.x, vec.y, vec.z);", + " this.colorBuffer.setXYZW(index, particle.color.x, particle.color.y, particle.color.z, particle.color.w);", + " if (system.worldSpace) {", + " this.sizeBuffer.setX(index, particle.size);", + " }", + " else {", + " if (particle.parentMatrix) {", + " this.sizeBuffer.setX(index, particle.size);", + " }", + " else {", + " this.sizeBuffer.setX(index, (particle.size * (Math.abs(scale.x) + Math.abs(scale.y) + Math.abs(scale.z))) / 3);", + " }", + " }", + " this.uvTileBuffer.setX(index, particle.uvTile);", + " if (this.settings.renderMode === RenderMode.StretchedBillBoard && this.velocityBuffer) {", + " let speedFactor = system.rendererEmitterSettings.speedFactor;", + " if (speedFactor === 0)", + " speedFactor = 0.001;", + " const lengthFactor = system.rendererEmitterSettings.lengthFactor;", + " let vec;", + " if (system.worldSpace) {", + " vec = particle.velocity;", + " }", + " else {", + " vec = this.vector_;", + " if (particle.parentMatrix) {", + " this.rotationMat2_.setFromMatrix4(particle.parentMatrix);", + " vec.copy(particle.velocity).applyMatrix3(this.rotationMat2_);", + " }", + " else {", + " vec.copy(particle.velocity).applyMatrix3(this.rotationMat_);", + " }", + " }", + " this.velocityBuffer.setXYZW(index, vec.x * speedFactor, vec.y * speedFactor, vec.z * speedFactor, lengthFactor);", + " }", + " }", + " });", + " this.geometry.instanceCount = index;", + " if (index > 0) {", + " this.offsetBuffer.clearUpdateRanges();", + " this.offsetBuffer.addUpdateRange(0, index * 3);", + " this.offsetBuffer.needsUpdate = true;", + " this.sizeBuffer.clearUpdateRanges();", + " this.sizeBuffer.addUpdateRange(0, index);", + " this.sizeBuffer.needsUpdate = true;", + " this.colorBuffer.clearUpdateRanges();", + " this.colorBuffer.addUpdateRange(0, index * 4);", + " this.colorBuffer.needsUpdate = true;", + " this.uvTileBuffer.clearUpdateRanges();", + " this.uvTileBuffer.addUpdateRange(0, index);", + " this.uvTileBuffer.needsUpdate = true;", + " if (this.settings.renderMode === RenderMode.StretchedBillBoard && this.velocityBuffer) {", + " this.velocityBuffer.clearUpdateRanges();", + " this.velocityBuffer.addUpdateRange(0, index * 4);", + " this.velocityBuffer.needsUpdate = true;", + " }", + " if (this.settings.renderMode === RenderMode.Mesh) {", + " this.rotationBuffer.clearUpdateRanges();", + " this.rotationBuffer.addUpdateRange(0, index * 4);", + " this.rotationBuffer.needsUpdate = true;", + " }", + " else if (this.settings.renderMode === RenderMode.StretchedBillBoard ||", + " this.settings.renderMode === RenderMode.HorizontalBillBoard ||", + " this.settings.renderMode === RenderMode.VerticalBillBoard ||", + " this.settings.renderMode === RenderMode.BillBoard) {", + " this.rotationBuffer.clearUpdateRanges();", + " this.rotationBuffer.addUpdateRange(0, index);", + " this.rotationBuffer.needsUpdate = true;", + " }", + " }", + " }", + " dispose() {", + " this.geometry.dispose();", + " }", + "}", + "", + "var trail_frag = `", + "", + "#include ", + "#include ", + "#include ", + "#include ", + "#include ", + "#include ", + "", + "uniform sampler2D alphaMap;", + "uniform float useAlphaMap;", + "uniform float visibility;", + "uniform float alphaTest;", + "uniform vec2 repeat;", + "", + "varying vec4 vColor;", + " ", + "void main() {", + " #include ", + " #include ", + "", + " vec4 c = vColor;", + " ", + " #ifdef USE_MAP", + " #ifdef USE_COLOR_AS_ALPHA", + " vec4 tex = texture2D( map, vUv * repeat );", + " c *= vec4(tex.rgb, tex.r);", + " #else", + " c *= texture2D( map, vUv * repeat );", + " #endif", + " #endif", + " if( useAlphaMap == 1. ) c.a *= texture2D( alphaMap, vUv * repeat ).a;", + " if( c.a < alphaTest ) discard;", + " gl_FragColor = c;", + "", + " #include ", + " #include ", + "}`;", + "", + "var trail_vert = `", + "#include ", + "#include ", + "#include ", + "#include ", + "#include ", + "#include ", + "", + "attribute vec3 previous;", + "attribute vec3 next;", + "attribute float side;", + "attribute float width;", + "", + "uniform vec2 resolution;", + "uniform float lineWidth;", + "uniform float sizeAttenuation;", + " ", + "vec2 fix(vec4 i, float aspect) {", + " vec2 res = i.xy / i.w;", + " res.x *= aspect;", + " return res;", + "}", + " ", + "void main() {", + "", + " ${uv_vertex_tile}", + " ", + " float aspect = resolution.x / resolution.y;", + "", + " vColor = color;", + "", + " mat4 m = projectionMatrix * modelViewMatrix;", + " vec4 finalPosition = m * vec4( position, 1.0 );", + " vec4 prevPos = m * vec4( previous, 1.0 );", + " vec4 nextPos = m * vec4( next, 1.0 );", + "", + " vec2 currentP = fix( finalPosition, aspect );", + " vec2 prevP = fix( prevPos, aspect );", + " vec2 nextP = fix( nextPos, aspect );", + "", + " float w = lineWidth * width;", + "", + " vec2 dir;", + " if( nextP == currentP ) dir = normalize( currentP - prevP );", + " else if( prevP == currentP ) dir = normalize( nextP - currentP );", + " else {", + " vec2 dir1 = normalize( currentP - prevP );", + " vec2 dir2 = normalize( nextP - currentP );", + " dir = normalize( dir1 + dir2 );", + "", + " vec2 perp = vec2( -dir1.y, dir1.x );", + " vec2 miter = vec2( -dir.y, dir.x );", + " //w = clamp( w / dot( miter, perp ), 0., 4., * lineWidth * width );", + "", + " }", + "", + " //vec2 normal = ( cross( vec3( dir, 0. ) vec3( 0., 0., 1. ) ) ).xy;", + " vec4 normal = vec4( -dir.y, dir.x, 0., 1. );", + " normal.xy *= .5 * w;", + " normal *= projectionMatrix;", + " if( sizeAttenuation == 0. ) {", + " normal.xy *= finalPosition.w;", + " normal.xy /= ( vec4( resolution, 0., 1. ) * projectionMatrix ).xy;", + " }", + "", + " finalPosition.xy += normal.xy * side;", + "", + " gl_Position = finalPosition;", + "", + "\t#include ", + "\t#include ", + "\t", + " vec4 mvPosition = modelViewMatrix * vec4( position, 1.0 );", + " ", + "\t#include ", + "}`;", + "", + "new THREE.Vector3(0, 0, 1);", + "class TrailBatch extends VFXBatch {", + " constructor(settings) {", + " super(settings);", + " this.vector_ = new THREE.Vector3();", + " this.vector2_ = new THREE.Vector3();", + " this.vector3_ = new THREE.Vector3();", + " this.quaternion_ = new THREE.Quaternion();", + " this.maxParticles = 10000;", + " this.setupBuffers();", + " this.rebuildMaterial();", + " }", + " setupBuffers() {", + " if (this.geometry)", + " this.geometry.dispose();", + " this.geometry = new THREE.BufferGeometry();", + " this.indexBuffer = new THREE.BufferAttribute(new Uint32Array(this.maxParticles * 6), 1);", + " this.indexBuffer.setUsage(THREE.DynamicDrawUsage);", + " this.geometry.setIndex(this.indexBuffer);", + " this.positionBuffer = new THREE.BufferAttribute(new Float32Array(this.maxParticles * 6), 3);", + " this.positionBuffer.setUsage(THREE.DynamicDrawUsage);", + " this.geometry.setAttribute('position', this.positionBuffer);", + " this.previousBuffer = new THREE.BufferAttribute(new Float32Array(this.maxParticles * 6), 3);", + " this.previousBuffer.setUsage(THREE.DynamicDrawUsage);", + " this.geometry.setAttribute('previous', this.previousBuffer);", + " this.nextBuffer = new THREE.BufferAttribute(new Float32Array(this.maxParticles * 6), 3);", + " this.nextBuffer.setUsage(THREE.DynamicDrawUsage);", + " this.geometry.setAttribute('next', this.nextBuffer);", + " this.widthBuffer = new THREE.BufferAttribute(new Float32Array(this.maxParticles * 2), 1);", + " this.widthBuffer.setUsage(THREE.DynamicDrawUsage);", + " this.geometry.setAttribute('width', this.widthBuffer);", + " this.sideBuffer = new THREE.BufferAttribute(new Float32Array(this.maxParticles * 2), 1);", + " this.sideBuffer.setUsage(THREE.DynamicDrawUsage);", + " this.geometry.setAttribute('side', this.sideBuffer);", + " this.uvBuffer = new THREE.BufferAttribute(new Float32Array(this.maxParticles * 4), 2);", + " this.uvBuffer.setUsage(THREE.DynamicDrawUsage);", + " this.geometry.setAttribute('uv', this.uvBuffer);", + " this.colorBuffer = new THREE.BufferAttribute(new Float32Array(this.maxParticles * 8), 4);", + " this.colorBuffer.setUsage(THREE.DynamicDrawUsage);", + " this.geometry.setAttribute('color', this.colorBuffer);", + " }", + " expandBuffers(target) {", + " while (target >= this.maxParticles) {", + " this.maxParticles *= 2;", + " }", + " this.setupBuffers();", + " }", + " rebuildMaterial() {", + " this.layers.mask = this.settings.layers.mask;", + " const uniforms = {", + " lineWidth: { value: 1 },", + " map: { value: null },", + " useMap: { value: 0 },", + " alphaMap: { value: null },", + " useAlphaMap: { value: 0 },", + " resolution: { value: new THREE.Vector2(1, 1) },", + " sizeAttenuation: { value: 1 },", + " visibility: { value: 1 },", + " alphaTest: { value: 0 },", + " repeat: { value: new THREE.Vector2(1, 1) },", + " };", + " const defines = {};", + " defines['USE_UV'] = '';", + " defines['USE_COLOR_ALPHA'] = '';", + " if (this.settings.material.map) {", + " defines['USE_MAP'] = '';", + " defines['MAP_UV'] = getMaterialUVChannelName(this.settings.material.map.channel);", + " uniforms['map'] = new THREE.Uniform(this.settings.material.map);", + " uniforms['mapTransform'] = new THREE.Uniform(new THREE.Matrix3().copy(this.settings.material.map.matrix));", + " }", + " if (this.settings.material.defines &&", + " this.settings.material.defines['USE_COLOR_AS_ALPHA'] !== undefined) {", + " defines['USE_COLOR_AS_ALPHA'] = '';", + " }", + " if (this.settings.renderMode === RenderMode.Trail) {", + " this.material = new THREE.ShaderMaterial({", + " uniforms: uniforms,", + " defines: defines,", + " vertexShader: trail_vert,", + " fragmentShader: trail_frag,", + " transparent: this.settings.material.transparent,", + " depthWrite: !this.settings.material.transparent,", + " side: this.settings.material.side,", + " blending: this.settings.material.blending || THREE.AdditiveBlending,", + " });", + " }", + " else {", + " throw new Error('render mode unavailable');", + " }", + " }", + " update() {", + " let index = 0;", + " let triangles = 0;", + " let particleCount = 0;", + " this.systems.forEach((system) => {", + " for (let j = 0; j < system.particleNum; j++) {", + " particleCount += system.particles[j].previous.length * 2;", + " }", + " });", + " if (particleCount > this.maxParticles) {", + " this.expandBuffers(particleCount);", + " }", + " this.systems.forEach((system) => {", + " const rotation = this.quaternion_;", + " const translation = this.vector2_;", + " const scale = this.vector3_;", + " system.emitter.matrixWorld.decompose(translation, rotation, scale);", + " const particles = system.particles;", + " const particleNum = system.particleNum;", + " const uTileCount = this.settings.uTileCount;", + " const vTileCount = this.settings.vTileCount;", + " const tileWidth = 1 / uTileCount;", + " const tileHeight = 1 / vTileCount;", + " for (let j = 0; j < particleNum; j++) {", + " const particle = particles[j];", + " const col = particle.uvTile % vTileCount;", + " const row = Math.floor(particle.uvTile / vTileCount + 0.001);", + " const iter = particle.previous.values();", + " let curIter = iter.next();", + " let previous = curIter.value;", + " let current = previous;", + " if (!curIter.done)", + " curIter = iter.next();", + " let next;", + " if (curIter.value !== undefined) {", + " next = curIter.value;", + " }", + " else {", + " next = current;", + " }", + " for (let i = 0; i < particle.previous.length; i++ , index += 2) {", + " this.positionBuffer.setXYZ(index, current.position.x, current.position.y, current.position.z);", + " this.positionBuffer.setXYZ(index + 1, current.position.x, current.position.y, current.position.z);", + " if (system.worldSpace) {", + " this.positionBuffer.setXYZ(index, current.position.x, current.position.y, current.position.z);", + " this.positionBuffer.setXYZ(index + 1, current.position.x, current.position.y, current.position.z);", + " }", + " else {", + " if (particle.parentMatrix) {", + " this.vector_.copy(current.position).applyMatrix4(particle.parentMatrix);", + " }", + " else {", + " this.vector_.copy(current.position).applyMatrix4(system.emitter.matrixWorld);", + " }", + " this.positionBuffer.setXYZ(index, this.vector_.x, this.vector_.y, this.vector_.z);", + " this.positionBuffer.setXYZ(index + 1, this.vector_.x, this.vector_.y, this.vector_.z);", + " }", + " if (system.worldSpace) {", + " this.previousBuffer.setXYZ(index, previous.position.x, previous.position.y, previous.position.z);", + " this.previousBuffer.setXYZ(index + 1, previous.position.x, previous.position.y, previous.position.z);", + " }", + " else {", + " if (particle.parentMatrix) {", + " this.vector_.copy(previous.position).applyMatrix4(particle.parentMatrix);", + " }", + " else {", + " this.vector_.copy(previous.position).applyMatrix4(system.emitter.matrixWorld);", + " }", + " this.previousBuffer.setXYZ(index, this.vector_.x, this.vector_.y, this.vector_.z);", + " this.previousBuffer.setXYZ(index + 1, this.vector_.x, this.vector_.y, this.vector_.z);", + " }", + " if (system.worldSpace) {", + " this.nextBuffer.setXYZ(index, next.position.x, next.position.y, next.position.z);", + " this.nextBuffer.setXYZ(index + 1, next.position.x, next.position.y, next.position.z);", + " }", + " else {", + " if (particle.parentMatrix) {", + " this.vector_.copy(next.position).applyMatrix4(particle.parentMatrix);", + " }", + " else {", + " this.vector_.copy(next.position).applyMatrix4(system.emitter.matrixWorld);", + " }", + " this.nextBuffer.setXYZ(index, this.vector_.x, this.vector_.y, this.vector_.z);", + " this.nextBuffer.setXYZ(index + 1, this.vector_.x, this.vector_.y, this.vector_.z);", + " }", + " this.sideBuffer.setX(index, -1);", + " this.sideBuffer.setX(index + 1, 1);", + " if (system.worldSpace) {", + " this.widthBuffer.setX(index, current.size);", + " this.widthBuffer.setX(index + 1, current.size);", + " }", + " else {", + " if (particle.parentMatrix) {", + " this.widthBuffer.setX(index, current.size);", + " this.widthBuffer.setX(index + 1, current.size);", + " }", + " else {", + " const objectScale = (Math.abs(scale.x) + Math.abs(scale.y) + Math.abs(scale.z)) / 3;", + " this.widthBuffer.setX(index, current.size * objectScale);", + " this.widthBuffer.setX(index + 1, current.size * objectScale);", + " }", + " }", + " this.uvBuffer.setXY(index, (i / particle.previous.length + col) * tileWidth, (vTileCount - row - 1) * tileHeight);", + " this.uvBuffer.setXY(index + 1, (i / particle.previous.length + col) * tileWidth, (vTileCount - row) * tileHeight);", + " this.colorBuffer.setXYZW(index, current.color.x, current.color.y, current.color.z, current.color.w);", + " this.colorBuffer.setXYZW(index + 1, current.color.x, current.color.y, current.color.z, current.color.w);", + " if (i + 1 < particle.previous.length) {", + " this.indexBuffer.setX(triangles * 3, index);", + " this.indexBuffer.setX(triangles * 3 + 1, index + 1);", + " this.indexBuffer.setX(triangles * 3 + 2, index + 2);", + " triangles++;", + " this.indexBuffer.setX(triangles * 3, index + 2);", + " this.indexBuffer.setX(triangles * 3 + 1, index + 1);", + " this.indexBuffer.setX(triangles * 3 + 2, index + 3);", + " triangles++;", + " }", + " previous = current;", + " current = next;", + " if (!curIter.done) {", + " curIter = iter.next();", + " if (curIter.value !== undefined) {", + " next = curIter.value;", + " }", + " }", + " }", + " }", + " });", + " this.positionBuffer.clearUpdateRanges();", + " this.positionBuffer.addUpdateRange(0, index * 3);", + " this.positionBuffer.needsUpdate = true;", + " this.previousBuffer.clearUpdateRanges();", + " this.previousBuffer.addUpdateRange(0, index * 3);", + " this.previousBuffer.needsUpdate = true;", + " this.nextBuffer.clearUpdateRanges();", + " this.nextBuffer.addUpdateRange(0, index * 3);", + " this.nextBuffer.needsUpdate = true;", + " this.sideBuffer.clearUpdateRanges();", + " this.sideBuffer.addUpdateRange(0, index);", + " this.sideBuffer.needsUpdate = true;", + " this.widthBuffer.clearUpdateRanges();", + " this.widthBuffer.addUpdateRange(0, index);", + " this.widthBuffer.needsUpdate = true;", + " this.uvBuffer.clearUpdateRanges();", + " this.uvBuffer.addUpdateRange(0, index * 2);", + " this.uvBuffer.needsUpdate = true;", + " this.colorBuffer.clearUpdateRanges();", + " this.colorBuffer.addUpdateRange(0, index * 4);", + " this.colorBuffer.needsUpdate = true;", + " this.indexBuffer.clearUpdateRanges();", + " this.indexBuffer.addUpdateRange(0, triangles * 3);", + " this.indexBuffer.needsUpdate = true;", + " this.geometry.setDrawRange(0, triangles * 3);", + " }", + " dispose() {", + " this.geometry.dispose();", + " }", + "}", + "", + "class BatchedRenderer extends THREE.Object3D {", + " constructor() {", + " super();", + " this.batches = [];", + " this.systemToBatchIndex = new Map();", + " this.type = 'BatchedRenderer';", + " }", + " static equals(a, b) {", + " return (a.material.side === b.material.side &&", + " a.material.blending === b.material.blending &&", + " a.material.transparent === b.material.transparent &&", + " a.material.type === b.material.type &&", + " a.material.alphaTest === b.material.alphaTest &&", + " a.material.map === b.material.map &&", + " a.renderMode === b.renderMode &&", + " a.uTileCount === b.uTileCount &&", + " a.vTileCount === b.vTileCount &&", + " a.instancingGeometry === b.instancingGeometry &&", + " a.renderOrder === b.renderOrder &&", + " a.layers.mask === b.layers.mask);", + " }", + " addSystem(system) {", + " system._renderer = this;", + " const settings = system.getRendererSettings();", + " for (let i = 0; i < this.batches.length; i++) {", + " if (BatchedRenderer.equals(this.batches[i].settings, settings)) {", + " this.batches[i].addSystem(system);", + " this.systemToBatchIndex.set(system, i);", + " return;", + " }", + " }", + " let batch;", + " switch (settings.renderMode) {", + " case RenderMode.Trail:", + " batch = new TrailBatch(settings);", + " break;", + " case RenderMode.Mesh:", + " case RenderMode.BillBoard:", + " case RenderMode.VerticalBillBoard:", + " case RenderMode.HorizontalBillBoard:", + " case RenderMode.StretchedBillBoard:", + " batch = new SpriteBatch(settings);", + " break;", + " }", + " batch.addSystem(system);", + " this.batches.push(batch);", + " this.systemToBatchIndex.set(system, this.batches.length - 1);", + " this.add(batch);", + " }", + " deleteSystem(system) {", + " const batchIndex = this.systemToBatchIndex.get(system);", + " if (batchIndex != undefined) {", + " this.batches[batchIndex].removeSystem(system);", + " this.systemToBatchIndex.delete(system);", + " }", + " }", + " updateSystem(system) {", + " this.deleteSystem(system);", + " this.addSystem(system);", + " }", + " update(delta) {", + " this.systemToBatchIndex.forEach((value, ps) => {", + " ps.update(delta);", + " });", + " for (let i = 0; i < this.batches.length; i++) {", + " this.batches[i].update();", + " }", + " }", + "}", + "", + "const BatchedParticleRenderer = BatchedRenderer;", + "", + "class QuarksLoader extends THREE.ObjectLoader {", + " constructor(manager) {", + " super(manager);", + " }", + " linkReference(object) {", + " const objectsMap = {};", + " object.traverse(function (child) {", + " objectsMap[child.uuid] = child;", + " });", + " object.traverse(function (child) {", + " if (child.type === 'ParticleEmitter') {", + " const system = child.system;", + " system.emitterShape;", + " for (let i = 0; i < system.behaviors.length; i++) {", + " if (system.behaviors[i] instanceof EmitSubParticleSystem) {", + " system.behaviors[i].subParticleSystem = objectsMap[system.behaviors[i].subParticleSystem];", + " }", + " }", + " }", + " });", + " }", + " parse(json, onLoad) {", + " const object = super.parse(json, onLoad);", + " this.linkReference(object);", + " return object;", + " }", + " parseObject(data, geometries, materials, textures, animations) {", + " let object;", + " function getGeometry(name) {", + " if (geometries[name] === undefined) {", + " console.warn('THREE.ObjectLoader: Undefined geometry', name);", + " }", + " return geometries[name];", + " }", + " function getMaterial(name) {", + " if (name === undefined)", + " return undefined;", + " if (Array.isArray(name)) {", + " const array = [];", + " for (let i = 0, l = name.length; i < l; i++) {", + " const uuid = name[i];", + " if (materials[uuid] === undefined) {", + " console.warn('THREE.ObjectLoader: Undefined material', uuid);", + " }", + " array.push(materials[uuid]);", + " }", + " return array;", + " }", + " if (materials[name] === undefined) {", + " console.warn('THREE.ObjectLoader: Undefined material', name);", + " }", + " return materials[name];", + " }", + " function getTexture(uuid) {", + " if (textures[uuid] === undefined) {", + " console.warn('THREE.ObjectLoader: Undefined texture', uuid);", + " }", + " return textures[uuid];", + " }", + " let geometry, material;", + " const meta = {", + " textures: textures,", + " geometries: geometries,", + " materials: materials,", + " };", + " const dependencies = {};", + " switch (data.type) {", + " case 'ParticleEmitter':", + " object = ParticleSystem.fromJSON(data.ps, meta, dependencies).emitter;", + " break;", + " case 'Scene':", + " object = new THREE.Scene();", + " if (data.background !== undefined) {", + " if (Number.isInteger(data.background)) {", + " object.background = new THREE.Color(data.background);", + " }", + " else {", + " object.background = getTexture(data.background);", + " }", + " }", + " if (data.environment !== undefined) {", + " object.environment = getTexture(data.environment);", + " }", + " if (data.fog !== undefined) {", + " if (data.fog.type === 'Fog') {", + " object.fog = new THREE.Fog(data.fog.color, data.fog.near, data.fog.far);", + " }", + " else if (data.fog.type === 'FogExp2') {", + " object.fog = new THREE.FogExp2(data.fog.color, data.fog.density);", + " }", + " }", + " if (data.backgroundBlurriness !== undefined)", + " object.backgroundBlurriness = data.backgroundBlurriness;", + " break;", + " case 'PerspectiveCamera':", + " object = new THREE.PerspectiveCamera(data.fov, data.aspect, data.near, data.far);", + " if (data.focus !== undefined)", + " object.focus = data.focus;", + " if (data.zoom !== undefined)", + " object.zoom = data.zoom;", + " if (data.filmGauge !== undefined)", + " object.filmGauge = data.filmGauge;", + " if (data.filmOffset !== undefined)", + " object.filmOffset = data.filmOffset;", + " if (data.view !== undefined)", + " object.view = Object.assign({}, data.view);", + " break;", + " case 'OrthographicCamera':", + " object = new THREE.OrthographicCamera(data.left, data.right, data.top, data.bottom, data.near, data.far);", + " if (data.zoom !== undefined)", + " object.zoom = data.zoom;", + " if (data.view !== undefined)", + " object.view = Object.assign({}, data.view);", + " break;", + " case 'AmbientLight':", + " object = new THREE.AmbientLight(data.color, data.intensity);", + " break;", + " case 'DirectionalLight':", + " object = new THREE.DirectionalLight(data.color, data.intensity);", + " break;", + " case 'PointLight':", + " object = new THREE.PointLight(data.color, data.intensity, data.distance, data.decay);", + " break;", + " case 'RectAreaLight':", + " object = new THREE.RectAreaLight(data.color, data.intensity, data.width, data.height);", + " break;", + " case 'SpotLight':", + " object = new THREE.SpotLight(data.color, data.intensity, data.distance, data.angle, data.penumbra, data.decay);", + " break;", + " case 'HemisphereLight':", + " object = new THREE.HemisphereLight(data.color, data.groundColor, data.intensity);", + " break;", + " case 'LightProbe':", + " object = new THREE.LightProbe().fromJSON(data);", + " break;", + " case 'SkinnedMesh':", + " geometry = getGeometry(data.geometry);", + " material = getMaterial(data.material);", + " object = new THREE.SkinnedMesh(geometry, material);", + " if (data.bindMode !== undefined)", + " object.bindMode = data.bindMode;", + " if (data.bindMatrix !== undefined)", + " object.bindMatrix.fromArray(data.bindMatrix);", + " if (data.skeleton !== undefined)", + " object.skeleton = data.skeleton;", + " break;", + " case 'Mesh':", + " geometry = getGeometry(data.geometry);", + " material = getMaterial(data.material);", + " object = new THREE.Mesh(geometry, material);", + " break;", + " case 'InstancedMesh': {", + " geometry = getGeometry(data.geometry);", + " material = getMaterial(data.material);", + " const count = data.count;", + " const instanceMatrix = data.instanceMatrix;", + " const instanceColor = data.instanceColor;", + " object = new THREE.InstancedMesh(geometry, material, count);", + " object.instanceMatrix = new THREE.InstancedBufferAttribute(new Float32Array(instanceMatrix.array), 16);", + " if (instanceColor !== undefined)", + " object.instanceColor = new THREE.InstancedBufferAttribute(new Float32Array(instanceColor.array), instanceColor.itemSize);", + " break;", + " }", + " case 'LOD':", + " object = new THREE.LOD();", + " break;", + " case 'Line':", + " object = new THREE.Line(getGeometry(data.geometry), getMaterial(data.material));", + " break;", + " case 'LineLoop':", + " object = new THREE.LineLoop(getGeometry(data.geometry), getMaterial(data.material));", + " break;", + " case 'LineSegments':", + " object = new THREE.LineSegments(getGeometry(data.geometry), getMaterial(data.material));", + " break;", + " case 'PointCloud':", + " case 'Points':", + " object = new THREE.Points(getGeometry(data.geometry), getMaterial(data.material));", + " break;", + " case 'Sprite':", + " object = new THREE.Sprite(getMaterial(data.material));", + " break;", + " case 'Group':", + " object = new THREE.Group();", + " break;", + " case 'Bone':", + " object = new THREE.Bone();", + " break;", + " default:", + " object = new THREE.Object3D();", + " }", + " object.uuid = data.uuid;", + " if (data.name !== undefined)", + " object.name = data.name;", + " if (data.matrix !== undefined) {", + " object.matrix.fromArray(data.matrix);", + " if (data.matrixAutoUpdate !== undefined)", + " object.matrixAutoUpdate = data.matrixAutoUpdate;", + " if (object.matrixAutoUpdate)", + " object.matrix.decompose(object.position, object.quaternion, object.scale);", + " }", + " else {", + " if (data.position !== undefined)", + " object.position.fromArray(data.position);", + " if (data.rotation !== undefined)", + " object.rotation.fromArray(data.rotation);", + " if (data.quaternion !== undefined)", + " object.quaternion.fromArray(data.quaternion);", + " if (data.scale !== undefined)", + " object.scale.fromArray(data.scale);", + " }", + " if (data.castShadow !== undefined)", + " object.castShadow = data.castShadow;", + " if (data.receiveShadow !== undefined)", + " object.receiveShadow = data.receiveShadow;", + " if (data.shadow) {", + " if (data.shadow.bias !== undefined)", + " object.shadow.bias = data.shadow.bias;", + " if (data.shadow.normalBias !== undefined)", + " object.normalBias = data.shadow.normalBias;", + " if (data.shadow.radius !== undefined)", + " object.radius = data.shadow.radius;", + " if (data.shadow.mapSize !== undefined)", + " object.mapSize.fromArray(data.shadow.mapSize);", + " if (data.shadow.camera !== undefined) {", + " object.camera = this.parseObject(data.shadow.camera);", + " }", + " }", + " if (data.visible !== undefined)", + " object.visible = data.visible;", + " if (data.frustumCulled !== undefined)", + " object.frustumCulled = data.frustumCulled;", + " if (data.renderOrder !== undefined)", + " object.renderOrder = data.renderOrder;", + " if (data.userData !== undefined)", + " object.userData = data.userData;", + " if (data.layers !== undefined)", + " object.layers.mask = data.layers;", + " if (data.children !== undefined) {", + " const children = data.children;", + " for (let i = 0; i < children.length; i++) {", + " object.add(this.parseObject(children[i], geometries, materials, textures, animations));", + " }", + " }", + " if (data.animations !== undefined) {", + " const objectAnimations = data.animations;", + " for (let i = 0; i < objectAnimations.length; i++) {", + " const uuid = objectAnimations[i];", + " object.animations.push(animations[uuid]);", + " }", + " }", + " if (data.type === 'LOD') {", + " if (data.autoUpdate !== undefined)", + " object.autoUpdate = data.autoUpdate;", + " const levels = data.levels;", + " for (let l = 0; l < levels.length; l++) {", + " const level = levels[l];", + " const child = object.getObjectByProperty('uuid', level.object);", + " if (child !== undefined) {", + " object.addLevel(child, level.distance);", + " }", + " }", + " }", + " return object;", + " }", + "}", + "", + "const Plugins = [];", + "function loadPlugin(plugin) {", + " const existing = Plugins.find((item) => item.id === plugin.id);", + " if (!existing) {", + " plugin.initialize();", + " for (const emitterShape of plugin.emitterShapes) {", + " if (!EmitterShapes[emitterShape.type]) {", + " EmitterShapes[emitterShape.type] = emitterShape;", + " }", + " }", + " for (const behavior of plugin.behaviors) {", + " if (!BehaviorTypes[behavior.type]) {", + " BehaviorTypes[behavior.type] = behavior;", + " }", + " }", + " }", + "}", + "function unloadPlugin(pluginId) {", + " const plugin = Plugins.find((item) => item.id === pluginId);", + " if (plugin) {", + " for (const emitterShape of plugin.emitterShapes) {", + " if (EmitterShapes[emitterShape.type]) {", + " delete EmitterShapes[emitterShape.type];", + " }", + " }", + " for (const behavior of plugin.behaviors) {", + " if (BehaviorTypes[behavior.type]) {", + " delete BehaviorTypes[behavior.type];", + " }", + " }", + " }", + "}", + "", + "let NodeValueType = void 0;", + "(function (NodeValueType) {", + " NodeValueType[NodeValueType[\"Number\"] = 0] = \"Number\";", + " NodeValueType[NodeValueType[\"Vec2\"] = 1] = \"Vec2\";", + " NodeValueType[NodeValueType[\"Vec3\"] = 2] = \"Vec3\";", + " NodeValueType[NodeValueType[\"Vec4\"] = 3] = \"Vec4\";", + " NodeValueType[NodeValueType[\"Boolean\"] = 4] = \"Boolean\";", + " NodeValueType[NodeValueType[\"AnyType\"] = 5] = \"AnyType\";", + " NodeValueType[NodeValueType[\"NullableAnyType\"] = 6] = \"NullableAnyType\";", + " NodeValueType[NodeValueType[\"EventStream\"] = 7] = \"EventStream\";", + "})(NodeValueType || (NodeValueType = {}));", + "const genDefaultForNodeValueType = (type) => {", + " switch (type) {", + " case NodeValueType.Boolean:", + " return false;", + " case NodeValueType.Number:", + " return 0;", + " case NodeValueType.Vec2:", + " return new THREE.Vector2();", + " case NodeValueType.Vec3:", + " return new THREE.Vector3();", + " case NodeValueType.Vec4:", + " return new THREE.Vector4();", + " case NodeValueType.AnyType:", + " return 0;", + " case NodeValueType.NullableAnyType:", + " return undefined;", + " }", + "};", + "", + "class Node {", + " constructor(type, signatureIndex = -1, data = {}) {", + " this.inputs = [];", + " this.outputs = [];", + " this.signatureIndex = -1;", + " this.position = new THREE.Vector2();", + " this.outputValues = [];", + " this.id = '' + Math.round(Math.random() * 100000);", + " this.type = type;", + " this.signatureIndex = signatureIndex;", + " this.data = data;", + " const realIndex = signatureIndex === -1 ? 0 : signatureIndex;", + " for (let i = 0; i < type.nodeTypeSignatures[realIndex].inputTypes.length; i++) {", + " this.inputs.push(undefined);", + " }", + " for (let i = 0; i < type.nodeTypeSignatures[realIndex].outputTypes.length; i++) {", + " this.outputs.push([]);", + " this.outputValues.push(genDefaultForNodeValueType(type.nodeTypeSignatures[realIndex].outputTypes[i]));", + " }", + " }", + " get inputTypes() {", + " const signatureIndex = this.signatureIndex === -1 ? 0 : this.signatureIndex;", + " return this.type.nodeTypeSignatures[signatureIndex].inputTypes;", + " }", + " get outputTypes() {", + " const signatureIndex = this.signatureIndex === -1 ? 0 : this.signatureIndex;", + " return this.type.nodeTypeSignatures[signatureIndex].outputTypes;", + " }", + " func(context, inputs, outputs) {", + " const signatureIndex = this.signatureIndex === -1 ? 0 : this.signatureIndex;", + " this.type.nodeTypeSignatures[signatureIndex].func(context, this.data, inputs, outputs);", + " }", + "}", + "class Wire {", + " constructor(input, inputIndex, output, outputIndex) {", + " this.input = input;", + " this.inputIndex = inputIndex;", + " this.input.outputs[inputIndex].push(this);", + " this.output = output;", + " this.outputIndex = outputIndex;", + " this.output.inputs[outputIndex] = this;", + " }", + "}", + "", + "class BaseCompiler {", + " constructor() {", + " this.visited = new Set();", + " BaseCompiler.Instance = this;", + " }", + " buildExecutionOrder(graph, context) {", + " graph.nodesInOrder.length = 0;", + " this.visited.clear();", + " for (let i = 0; i < graph.outputNodes.length; i++) {", + " const node = graph.outputNodes[i];", + " if (node.inputs[0] !== undefined) {", + " this._traverse(node, graph, context);", + " }", + " }", + " graph.compiled = true;", + " }", + " _traverse(node, graph, context) {", + " this.visited.add(node.id);", + " for (let i = 0; i < node.inputs.length; i++) {", + " if (node.inputs[i] instanceof Wire) {", + " const inputNode = node.inputs[i].input;", + " if (!this.visited.has(inputNode.id)) {", + " this._traverse(inputNode, graph, context);", + " }", + " }", + " else if (node.inputs[i] !== undefined);", + " else;", + " }", + " graph.nodesInOrder.push(node);", + " }", + "}", + "", + "class Interpreter extends BaseCompiler {", + " constructor() {", + " super();", + " }", + " executeCompiledGraph(graph, context) {", + " const nodes = graph.nodesInOrder;", + " for (let i = 0; i < nodes.length; i++) {", + " const inputValues = [];", + " const node = nodes[i];", + " for (let j = 0; j < node.inputs.length; j++) {", + " if (node.inputs[j] instanceof Wire) {", + " inputValues.push(node.inputs[j].input.outputValues[node.inputs[j].inputIndex]);", + " }", + " else if (node.inputs[j] !== undefined) {", + " inputValues.push(node.inputs[j].getValue(context));", + " }", + " else {", + " inputValues.push(undefined);", + " }", + " }", + " node.func(context, inputValues, node.outputValues);", + " }", + " }", + " run(graph, context) {", + " if (!graph.compiled) {", + " this.buildExecutionOrder(graph, context);", + " }", + " this.executeCompiledGraph(graph, context);", + " }", + "}", + "", + "class NodeType {", + " constructor(name) {", + " this.nodeTypeSignatures = [];", + " this.name = name;", + " }", + " addSignature(inputTypes, outputTypes, func) {", + " this.nodeTypeSignatures.push({", + " inputTypes: inputTypes,", + " outputTypes: outputTypes,", + " func: func,", + " });", + " }", + "}", + "class GraphNodeType extends NodeType {", + " constructor(nodeGraph) {", + " const inputTypes = [];", + " for (let i = 0; i < nodeGraph.inputNodes.length; i++) {", + " if (nodeGraph.inputNodes[i].type.name === 'input') {", + " inputTypes.push(nodeGraph.inputNodes[i].data.type);", + " }", + " }", + " const outputTypes = [];", + " for (let i = 0; i < nodeGraph.outputNodes.length; i++) {", + " if (nodeGraph.outputNodes[i].type.name === 'output') {", + " outputTypes.push(nodeGraph.outputNodes[i].data.type);", + " }", + " }", + " super(nodeGraph.name);", + " this.addSignature(inputTypes, outputTypes, (context, data, inputs, outputs) => {", + " context.inputs = inputs;", + " context.outputs = outputs;", + " Interpreter.Instance.run(nodeGraph, context);", + " });", + " this.nodeGraph = nodeGraph;", + " }", + "}", + "", + "const NodeTypes = {};", + "const addNode = new NodeType('add');", + "addNode.addSignature([NodeValueType.Number, NodeValueType.Number], [NodeValueType.Number], (context, data, inputs, outputs) => {", + " outputs[0] = inputs[0] + inputs[1];", + "});", + "addNode.addSignature([NodeValueType.Vec2, NodeValueType.Vec2], [NodeValueType.Vec2], (context, data, inputs, outputs) => {", + " outputs[0].addVectors(inputs[0], inputs[1]);", + "});", + "addNode.addSignature([NodeValueType.Vec3, NodeValueType.Vec3], [NodeValueType.Vec3], (context, data, inputs, outputs) => {", + " outputs[0].addVectors(inputs[0], inputs[1]);", + "});", + "addNode.addSignature([NodeValueType.Vec4, NodeValueType.Vec4], [NodeValueType.Vec4], (context, data, inputs, outputs) => {", + " outputs[0].addVectors(inputs[0], inputs[1]);", + "});", + "NodeTypes['add'] = addNode;", + "const subNode = new NodeType('sub');", + "subNode.addSignature([NodeValueType.Number, NodeValueType.Number], [NodeValueType.Number], (context, data, inputs, outputs) => {", + " outputs[0] = inputs[0] - inputs[1];", + "});", + "subNode.addSignature([NodeValueType.Vec2, NodeValueType.Vec2], [NodeValueType.Vec2], (context, data, inputs, outputs) => {", + " outputs[0].subVectors(inputs[0], inputs[1]);", + "});", + "subNode.addSignature([NodeValueType.Vec3, NodeValueType.Vec3], [NodeValueType.Vec3], (context, data, inputs, outputs) => {", + " outputs[0].subVectors(inputs[0], inputs[1]);", + "});", + "subNode.addSignature([NodeValueType.Vec4, NodeValueType.Vec4], [NodeValueType.Vec4], (context, data, inputs, outputs) => {", + " outputs[0].subVectors(inputs[0], inputs[1]);", + "});", + "NodeTypes['sub'] = subNode;", + "const mulNode = new NodeType('mul');", + "mulNode.addSignature([NodeValueType.Number, NodeValueType.Number], [NodeValueType.Number], (context, data, inputs, outputs) => {", + " outputs[0] = inputs[0] * inputs[1];", + "});", + "mulNode.addSignature([NodeValueType.Vec2, NodeValueType.Number], [NodeValueType.Vec2], (context, data, inputs, outputs) => {", + " outputs[0].copy(inputs[0]).multiplyScalar(inputs[1]);", + "});", + "mulNode.addSignature([NodeValueType.Vec3, NodeValueType.Number], [NodeValueType.Vec3], (context, data, inputs, outputs) => {", + " outputs[0].copy(inputs[0]).multiplyScalar(inputs[1]);", + "});", + "mulNode.addSignature([NodeValueType.Vec4, NodeValueType.Number], [NodeValueType.Vec4], (context, data, inputs, outputs) => {", + " outputs[0].copy(inputs[0]).multiplyScalar(inputs[1]);", + "});", + "NodeTypes['mul'] = mulNode;", + "const divNode = new NodeType('div');", + "divNode.addSignature([NodeValueType.Number, NodeValueType.Number], [NodeValueType.Number], (context, data, inputs, outputs) => {", + " outputs[0] = inputs[0] / inputs[1];", + "});", + "divNode.addSignature([NodeValueType.Vec2, NodeValueType.Number], [NodeValueType.Vec2], (context, data, inputs, outputs) => {", + " outputs[0].copy(inputs[0]).divideScalar(inputs[1]);", + "});", + "divNode.addSignature([NodeValueType.Vec3, NodeValueType.Number], [NodeValueType.Vec3], (context, data, inputs, outputs) => {", + " outputs[0].copy(inputs[0]).divideScalar(inputs[1]);", + "});", + "divNode.addSignature([NodeValueType.Vec4, NodeValueType.Number], [NodeValueType.Vec4], (context, data, inputs, outputs) => {", + " outputs[0].copy(inputs[0]).divideScalar(inputs[1]);", + "});", + "NodeTypes['div'] = divNode;", + "const sinNode = new NodeType('sin');", + "sinNode.addSignature([NodeValueType.Number], [NodeValueType.Number], (context, data, inputs, outputs) => {", + " outputs[0] = Math.sin(inputs[0]);", + "});", + "NodeTypes['sin'] = sinNode;", + "const cosNode = new NodeType('cos');", + "cosNode.addSignature([NodeValueType.Number], [NodeValueType.Number], (context, data, inputs, outputs) => {", + " outputs[0] = Math.cos(inputs[0]);", + "});", + "NodeTypes['cos'] = cosNode;", + "const tanNode = new NodeType('tan');", + "tanNode.addSignature([NodeValueType.Number], [NodeValueType.Number], (context, data, inputs, outputs) => {", + " outputs[0] = Math.tan(inputs[0]);", + "});", + "NodeTypes['tan'] = tanNode;", + "const absNode = new NodeType('abs');", + "absNode.addSignature([NodeValueType.Number], [NodeValueType.Number], (context, data, inputs, outputs) => {", + " outputs[0] = Math.abs(inputs[0]);", + "});", + "NodeTypes['abs'] = absNode;", + "const minNode = new NodeType('min');", + "minNode.addSignature([NodeValueType.Number, NodeValueType.Number], [NodeValueType.Number], (context, data, inputs, outputs) => {", + " outputs[0] = Math.min(inputs[0], inputs[1]);", + "});", + "NodeTypes['min'] = minNode;", + "const maxNode = new NodeType('max');", + "maxNode.addSignature([NodeValueType.Number, NodeValueType.Number], [NodeValueType.Number], (context, data, inputs, outputs) => {", + " outputs[0] = Math.max(inputs[0], inputs[1]);", + "});", + "NodeTypes['max'] = maxNode;", + "const dot = new NodeType('dot');", + "dot.addSignature([NodeValueType.Vec2, NodeValueType.Vec2], [NodeValueType.Number], (context, data, inputs, outputs) => {", + " outputs[0] = inputs[0].dot(inputs[1]);", + "});", + "dot.addSignature([NodeValueType.Vec3, NodeValueType.Vec3], [NodeValueType.Number], (context, data, inputs, outputs) => {", + " outputs[0] = inputs[0].dot(inputs[1]);", + "});", + "dot.addSignature([NodeValueType.Vec4, NodeValueType.Vec4], [NodeValueType.Number], (context, data, inputs, outputs) => {", + " outputs[0] = inputs[0].dot(inputs[1]);", + "});", + "NodeTypes['dot'] = dot;", + "const cross = new NodeType('cross');", + "cross.addSignature([NodeValueType.Vec3, NodeValueType.Vec3], [NodeValueType.Vec3], (context, data, inputs, outputs) => {", + " outputs[0].crossVectors(inputs[0], inputs[1]);", + "});", + "NodeTypes['cross'] = cross;", + "const length = new NodeType('length');", + "length.addSignature([NodeValueType.Vec2], [NodeValueType.Number], (context, data, inputs, outputs) => {", + " outputs[0] = inputs[0].length();", + "});", + "length.addSignature([NodeValueType.Vec3], [NodeValueType.Number], (context, data, inputs, outputs) => {", + " outputs[0] = inputs[0].length();", + "});", + "length.addSignature([NodeValueType.Vec4], [NodeValueType.Number], (context, data, inputs, outputs) => {", + " outputs[0] = inputs[0].length();", + "});", + "NodeTypes['length'] = length;", + "const lengthSq = new NodeType('lengthSq');", + "lengthSq.addSignature([NodeValueType.Vec2], [NodeValueType.Number], (context, data, inputs, outputs) => {", + " outputs[0] = inputs[0].lengthSq();", + "});", + "lengthSq.addSignature([NodeValueType.Vec3], [NodeValueType.Number], (context, data, inputs, outputs) => {", + " outputs[0] = inputs[0].lengthSq();", + "});", + "lengthSq.addSignature([NodeValueType.Vec4], [NodeValueType.Number], (context, data, inputs, outputs) => {", + " outputs[0] = inputs[0].lengthSq();", + "});", + "NodeTypes['lengthSq'] = lengthSq;", + "const normalize = new NodeType('normalize');", + "normalize.addSignature([NodeValueType.Vec2], [NodeValueType.Vec2], (context, data, inputs, outputs) => {", + " outputs[0].copy(inputs[0]).normalize();", + "});", + "normalize.addSignature([NodeValueType.Vec3], [NodeValueType.Vec3], (context, data, inputs, outputs) => {", + " outputs[0].copy(inputs[0]).normalize();", + "});", + "normalize.addSignature([NodeValueType.Vec4], [NodeValueType.Vec4], (context, data, inputs, outputs) => {", + " outputs[0].copy(inputs[0]).normalize();", + "});", + "NodeTypes['normalize'] = normalize;", + "const distance = new NodeType('distance');", + "distance.addSignature([NodeValueType.Vec2, NodeValueType.Vec2], [NodeValueType.Number], (context, data, inputs, outputs) => {", + " outputs[0] = inputs[0].distanceTo(inputs[1]);", + "});", + "distance.addSignature([NodeValueType.Vec3, NodeValueType.Vec3], [NodeValueType.Number], (context, data, inputs, outputs) => {", + " outputs[0] = inputs[0].distanceTo(inputs[1]);", + "});", + "NodeTypes['distance'] = distance;", + "const andNode = new NodeType('and');", + "andNode.addSignature([NodeValueType.Boolean, NodeValueType.Boolean], [NodeValueType.Boolean], (context, data, inputs, outputs) => {", + " outputs[0] = inputs[0] && inputs[1];", + "});", + "NodeTypes['and'] = andNode;", + "const orNode = new NodeType('or');", + "orNode.addSignature([NodeValueType.Boolean, NodeValueType.Boolean], [NodeValueType.Boolean], (context, data, inputs, outputs) => {", + " outputs[0] = inputs[0] || inputs[1];", + "});", + "NodeTypes['or'] = orNode;", + "const notNode = new NodeType('not');", + "notNode.addSignature([NodeValueType.Boolean], [NodeValueType.Boolean], (context, data, inputs, outputs) => {", + " outputs[0] = !inputs[0];", + "});", + "NodeTypes['not'] = notNode;", + "const equalNode = new NodeType('equal');", + "equalNode.addSignature([NodeValueType.Number, NodeValueType.Number], [NodeValueType.Boolean], (context, data, inputs, outputs) => {", + " outputs[0] = inputs[0] === inputs[1];", + "});", + "equalNode.addSignature([NodeValueType.Vec2, NodeValueType.Vec2], [NodeValueType.Boolean], (context, data, inputs, outputs) => {", + " outputs[0] = inputs[0].equals(inputs[1]);", + "});", + "equalNode.addSignature([NodeValueType.Vec3, NodeValueType.Vec3], [NodeValueType.Boolean], (context, data, inputs, outputs) => {", + " outputs[0] = inputs[0].equals(inputs[1]);", + "});", + "equalNode.addSignature([NodeValueType.Vec4, NodeValueType.Vec4], [NodeValueType.Boolean], (context, data, inputs, outputs) => {", + " outputs[0] = inputs[0].equals(inputs[1]);", + "});", + "NodeTypes['equal'] = equalNode;", + "const lessThanNode = new NodeType('lessThan');", + "lessThanNode.addSignature([NodeValueType.Number, NodeValueType.Number], [NodeValueType.Boolean], (context, data, inputs, outputs) => {", + " outputs[0] = inputs[0] < inputs[1];", + "});", + "NodeTypes['lessThan'] = lessThanNode;", + "const greaterThanNode = new NodeType('greaterThan');", + "greaterThanNode.addSignature([NodeValueType.Number, NodeValueType.Number], [NodeValueType.Boolean], (context, data, inputs, outputs) => {", + " outputs[0] = inputs[0] > inputs[1];", + "});", + "NodeTypes['greaterThan'] = greaterThanNode;", + "const lessThanOrEqualNode = new NodeType('lessThanOrEqual');", + "lessThanOrEqualNode.addSignature([NodeValueType.Number, NodeValueType.Number], [NodeValueType.Boolean], (context, data, inputs, outputs) => {", + " outputs[0] = inputs[0] <= inputs[1];", + "});", + "NodeTypes['lessThanOrEqual'] = lessThanOrEqualNode;", + "const greaterThanOrEqualNode = new NodeType('greaterThanOrEqual');", + "greaterThanOrEqualNode.addSignature([NodeValueType.Number, NodeValueType.Number], [NodeValueType.Boolean], (context, data, inputs, outputs) => {", + " outputs[0] = inputs[0] >= inputs[1];", + "});", + "NodeTypes['greaterThanOrEqual'] = greaterThanOrEqualNode;", + "const ifNode = new NodeType('if');", + "ifNode.addSignature([NodeValueType.Boolean, NodeValueType.AnyType, NodeValueType.AnyType], [NodeValueType.AnyType], (context, data, inputs, outputs) => {", + " outputs[0] = inputs[0] ? inputs[1] : inputs[2];", + "});", + "NodeTypes['if'] = ifNode;", + "const numberNode = new NodeType('number');", + "numberNode.addSignature([], [NodeValueType.Number], (context, data, inputs, outputs) => {", + " outputs[0] = data.value;", + "});", + "NodeTypes['number'] = numberNode;", + "const vec2Node = new NodeType('vec2');", + "vec2Node.addSignature([NodeValueType.Number, NodeValueType.Number], [NodeValueType.Vec2], (context, data, inputs, outputs) => {", + " outputs[0].x = inputs[0];", + " outputs[0].y = inputs[1];", + "});", + "NodeTypes['vec2'] = vec2Node;", + "const vec3Node = new NodeType('vec3');", + "vec3Node.addSignature([NodeValueType.Number, NodeValueType.Number, NodeValueType.Number], [NodeValueType.Vec3], (context, data, inputs, outputs) => {", + " outputs[0].x = inputs[0];", + " outputs[0].y = inputs[1];", + " outputs[0].z = inputs[2];", + "});", + "NodeTypes['vec3'] = vec3Node;", + "const vec4Node = new NodeType('vec4');", + "vec4Node.addSignature([NodeValueType.Number, NodeValueType.Number, NodeValueType.Number, NodeValueType.Number], [NodeValueType.Vec4], (context, data, inputs, outputs) => {", + " outputs[0].x = inputs[0];", + " outputs[0].y = inputs[1];", + " outputs[0].z = inputs[2];", + " outputs[0].w = inputs[3];", + "});", + "NodeTypes['vec4'] = vec4Node;", + "const splitVec2Node = new NodeType('splitVec2');", + "splitVec2Node.addSignature([NodeValueType.Vec2], [NodeValueType.Number, NodeValueType.Number], (context, data, inputs, outputs) => {", + " outputs[0] = inputs[0].x;", + " outputs[1] = inputs[0].y;", + "});", + "NodeTypes['splitVec2'] = splitVec2Node;", + "const splitVec3Node = new NodeType('splitVec3');", + "splitVec3Node.addSignature([NodeValueType.Vec3], [NodeValueType.Number, NodeValueType.Number, NodeValueType.Number], (context, data, inputs, outputs) => {", + " outputs[0] = inputs[0].x;", + " outputs[1] = inputs[0].y;", + " outputs[2] = inputs[0].z;", + "});", + "NodeTypes['splitVec3'] = splitVec3Node;", + "const splitVec4Node = new NodeType('splitVec4');", + "splitVec4Node.addSignature([NodeValueType.Vec4], [NodeValueType.Number, NodeValueType.Number, NodeValueType.Number, NodeValueType.Number], (context, data, inputs, outputs) => {", + " outputs[0] = inputs[0].x;", + " outputs[1] = inputs[0].y;", + " outputs[2] = inputs[0].z;", + " outputs[3] = inputs[0].w;", + "});", + "NodeTypes['splitVec4'] = splitVec4Node;", + "const boolNode = new NodeType('bool');", + "boolNode.addSignature([], [NodeValueType.Boolean], (context, data, inputs, outputs) => {", + " outputs[0] = data.value;", + "});", + "NodeTypes['bool'] = boolNode;", + "const particlePropertyNode = new NodeType('particleProperty');", + "particlePropertyNode.addSignature([NodeValueType.NullableAnyType], [NodeValueType.NullableAnyType], (context, data, inputs, outputs) => {", + " if (inputs[0] !== undefined) {", + " if (typeof inputs[0] === 'object') {", + " context.particle[data.property].copy(inputs[0]);", + " }", + " else {", + " context.particle[data.property] = inputs[0];", + " }", + " }", + " if (context.particle[data.property] !== undefined) {", + " if (typeof outputs[0] === 'object') {", + " outputs[0].copy(context.particle[data.property]);", + " }", + " else {", + " outputs[0] = context.particle[data.property];", + " }", + " }", + "});", + "NodeTypes['particleProperty'] = particlePropertyNode;", + "const emitNode = new NodeType('emit');", + "emitNode.addSignature([NodeValueType.EventStream], [], (context, data, inputs, outputs) => {", + " const arr = inputs[0];", + " for (let i = 0; i < arr.length; i++) {", + " context.signal(i, arr[i]);", + " }", + "});", + "NodeTypes['emit'] = emitNode;", + "const graphPropertyNode = new NodeType('graphProperty');", + "graphPropertyNode.addSignature([NodeValueType.NullableAnyType], [NodeValueType.NullableAnyType], (context, data, inputs, outputs) => {", + " if (inputs[0] !== undefined) {", + " if (typeof inputs[0] === 'object') {", + " context.graph[data.property].copy(inputs[0]);", + " }", + " else {", + " context.graph[data.property] = inputs[0];", + " }", + " }", + " if (context.graph[data.property] !== undefined) {", + " if (typeof outputs[0] === 'object') {", + " outputs[0].copy(context.graph[data.property]);", + " }", + " else {", + " outputs[0] = context.graph[data.property];", + " }", + " }", + "});", + "NodeTypes['graphProperty'] = graphPropertyNode;", + "const startEventNode = new NodeType('startEvent');", + "startEventNode.addSignature([], [NodeValueType.EventStream], (context, data, inputs, outputs) => {", + " outputs[0] = [{ type: 'start' }];", + "});", + "NodeTypes['startEvent'] = startEventNode;", + "const repeaterNode = new NodeType('repeater');", + "repeaterNode.addSignature([NodeValueType.EventStream, NodeValueType.Number], [NodeValueType.EventStream], (context, data, inputs, outputs) => {", + " const arr = inputs[0];", + " const count = inputs[1];", + " const result = [];", + " for (let j = 0; j < arr.length; j++) {", + " for (let i = 0; i < count; i++) {", + " result.push(arr[j]);", + " }", + " }", + " outputs[0] = result;", + "});", + "NodeTypes['repeater'] = repeaterNode;", + "const timeNode = new NodeType('time');", + "timeNode.addSignature([], [NodeValueType.Number], (context, data, inputs, outputs) => {", + " outputs[0] = context.emissionState.time;", + "});", + "NodeTypes['time'] = timeNode;", + "const deltaNode = new NodeType('delta');", + "deltaNode.addSignature([], [NodeValueType.Number], (context, data, inputs, outputs) => {", + " outputs[0] = context.delta;", + "});", + "NodeTypes['delta'] = deltaNode;", + "const outputNode = new NodeType('output');", + "outputNode.addSignature([NodeValueType.Number], [NodeValueType.Number], (context, data, inputs, outputs) => {", + " outputs[0] = inputs[0];", + "});", + "outputNode.addSignature([NodeValueType.Vec2], [NodeValueType.Vec2], (context, data, inputs, outputs) => {", + " outputs[0] = inputs[0];", + "});", + "outputNode.addSignature([NodeValueType.Vec3], [NodeValueType.Vec3], (context, data, inputs, outputs) => {", + " outputs[0] = inputs[0];", + "});", + "outputNode.addSignature([NodeValueType.Vec4], [NodeValueType.Vec4], (context, data, inputs, outputs) => {", + " outputs[0] = inputs[0];", + "});", + "outputNode.addSignature([NodeValueType.Boolean], [NodeValueType.Boolean], (context, data, inputs, outputs) => {", + " outputs[0] = inputs[0];", + "});", + "NodeTypes['output'] = outputNode;", + "const lerpNode = new NodeType('lerp');", + "lerpNode.addSignature([NodeValueType.Number, NodeValueType.Number, NodeValueType.Number], [NodeValueType.Number], (context, data, inputs, outputs) => {", + " outputs[0] =", + " inputs[0] * (1 - inputs[2]) + inputs[1] * inputs[2];", + "});", + "lerpNode.addSignature([NodeValueType.Vec2, NodeValueType.Vec2, NodeValueType.Number], [NodeValueType.Vec2], (context, data, inputs, outputs) => {", + " outputs[0].lerpVectors(inputs[0], inputs[1], inputs[2]);", + "});", + "lerpNode.addSignature([NodeValueType.Vec3, NodeValueType.Vec3, NodeValueType.Number], [NodeValueType.Vec3], (context, data, inputs, outputs) => {", + " outputs[0].lerpVectors(inputs[0], inputs[1], inputs[2]);", + "});", + "lerpNode.addSignature([NodeValueType.Vec4, NodeValueType.Vec4, NodeValueType.Number], [NodeValueType.Vec4], (context, data, inputs, outputs) => {", + " outputs[0].lerpVectors(inputs[0], inputs[1], inputs[2]);", + "});", + "NodeTypes['lerp'] = lerpNode;", + "const normalD = (x) => {", + " return (1 / Math.sqrt(2 * Math.PI)) * Math.exp(x * x * -0.5);", + "};", + "const normalDistributionNode = new NodeType('normDistrib');", + "normalDistributionNode.addSignature([NodeValueType.Number], [NodeValueType.Number], (context, data, inputs, outputs) => {", + " outputs[0] = normalD(inputs[0]);", + "});", + "NodeTypes['normDistrib'] = normalDistributionNode;", + "const normcdfNode = new NodeType('normcdf');", + "normcdfNode.addSignature([NodeValueType.Number], [NodeValueType.Number], (context, data, inputs, outputs) => {", + " let x = inputs[0];", + " const a1 = 0.254829592;", + " const a2 = -0.284496736;", + " const a3 = 1.421413741;", + " const a4 = -1.453152027;", + " const a5 = 1.061405429;", + " const p = 0.3275911;", + " let sign = 1;", + " if (x < 0)", + " sign = -1;", + " x = Math.abs(x) / Math.sqrt(2.0);", + " const t = 1.0 / (1.0 + p * x);", + " const y = 1.0 - ((((a5 * t + a4) * t + a3) * t + a2) * t + a1) * t * Math.exp(-x * x);", + " outputs[0] = 0.5 * (1.0 + sign * y);", + "});", + "NodeTypes['normcdf'] = normcdfNode;", + "const normcdfInvNode = new NodeType('normcdfInv');", + "const rationalApproximation = (t) => {", + " const c = [2.515517, 0.802853, 0.010328];", + " const d = [1.432788, 0.189269, 0.001308];", + " return t - ((c[2] * t + c[1]) * t + c[0]) / (((d[2] * t + d[1]) * t + d[0]) * t + 1.0);", + "};", + "const normcdfInv = (p) => {", + " if (p < 0.5) {", + " return -rationalApproximation(Math.sqrt(-2.0 * Math.log(p)));", + " }", + " else {", + " return rationalApproximation(Math.sqrt(-2.0 * Math.log(1 - p)));", + " }", + "};", + "normcdfInvNode.addSignature([NodeValueType.Number], [NodeValueType.Number], (context, data, inputs, outputs) => {", + " outputs[0] = normcdfInv(inputs[0]);", + "});", + "NodeTypes['normcdfInv'] = normcdfInvNode;", + "const clampNode = new NodeType('clamp');", + "clampNode.addSignature([NodeValueType.Number, NodeValueType.Number, NodeValueType.Number], [NodeValueType.Number], (context, data, inputs, outputs) => {", + " outputs[0] = Math.max(Math.min(inputs[0], inputs[2]), inputs[1]);", + "});", + "NodeTypes['clamp'] = clampNode;", + "const smoothstepNode = new NodeType('smoothstep');", + "smoothstepNode.addSignature([NodeValueType.Number, NodeValueType.Number, NodeValueType.Number], [NodeValueType.Number], (context, data, inputs, outputs) => {", + " const x = Math.max(Math.min(inputs[0], inputs[2]), inputs[1]);", + " outputs[0] = x * x * (3 - 2 * x);", + "});", + "NodeTypes['smoothstep'] = smoothstepNode;", + "const randomNode = new NodeType('random');", + "randomNode.addSignature([NodeValueType.Number, NodeValueType.Number], [NodeValueType.Number], (context, data, inputs, outputs) => {", + " outputs[0] = Math.random() * (inputs[1] - inputs[0]) + inputs[0];", + "});", + "randomNode.addSignature([NodeValueType.Vec2, NodeValueType.Vec2], [NodeValueType.Vec2], (context, data, inputs, outputs) => {", + " let random = Math.random();", + " outputs[0].lerpVectors(inputs[0], inputs[1], random);", + "});", + "randomNode.addSignature([NodeValueType.Vec3, NodeValueType.Vec3], [NodeValueType.Vec3], (context, data, inputs, outputs) => {", + " let random = Math.random();", + " outputs[0].lerpVectors(inputs[0], inputs[1], random);", + "});", + "randomNode.addSignature([NodeValueType.Vec4, NodeValueType.Vec4], [NodeValueType.Vec4], (context, data, inputs, outputs) => {", + " let random = Math.random();", + " outputs[0].lerpVectors(inputs[0], inputs[1], random);", + "});", + "NodeTypes['random'] = randomNode;", + "const vrandNode = new NodeType('vrand');", + "vrandNode.addSignature([], [NodeValueType.Vec2], (context, data, inputs, outputs) => {", + " let x = normcdfInv(Math.random());", + " let y = normcdfInv(Math.random());", + " const mag = Math.sqrt(x * x + y * y);", + " outputs[0].set(x / mag, y / mag);", + "});", + "vrandNode.addSignature([], [NodeValueType.Vec3], (context, data, inputs, outputs) => {", + " let x = normcdfInv(Math.random());", + " let y = normcdfInv(Math.random());", + " let z = normcdfInv(Math.random());", + " const mag = Math.sqrt(x * x + y * y + z * z);", + " outputs[0].set(x / mag, y / mag, z / mag);", + "});", + "vrandNode.addSignature([], [NodeValueType.Vec4], (context, data, inputs, outputs) => {", + " let x = normcdfInv(Math.random());", + " let y = normcdfInv(Math.random());", + " let z = normcdfInv(Math.random());", + " let w = normcdfInv(Math.random());", + " const mag = Math.sqrt(x * x + y * y + z * z + w * w);", + " outputs[0].set(x / mag, y / mag, z / mag, w / mag);", + "});", + "NodeTypes['vrand'] = vrandNode;", + "const bsdfNode = new NodeType('bsdf');", + "bsdfNode.addSignature([NodeValueType.Vec3, NodeValueType.Vec3, NodeValueType.Vec3, NodeValueType.Number], [], (context, data, inputs, outputs) => { });", + "NodeTypes['bsdf'] = bsdfNode;", + "const OutputNodeTypeNames = new Set(['output', 'particleProperty', 'graphProperty', 'emit']);", + "", + "class NodeGraph {", + " constructor(name) {", + " this.inputNodes = [];", + " this.outputNodes = [];", + " this.allNodes = new Map();", + " this.wires = [];", + " this.compiled = false;", + " this.nodesInOrder = [];", + " this.version = '1.0';", + " this.uuid = THREE.MathUtils.generateUUID();", + " this.name = name;", + " this.revision = 0;", + " }", + " addWire(wire) {", + " this.wires.push(wire);", + " this.revision++;", + " }", + " addNode(node) {", + " this.allNodes.set(node.id, node);", + " if (node.type === NodeTypes['input']) {", + " this.inputNodes.push(node);", + " }", + " else if (OutputNodeTypeNames.has(node.type.name)) {", + " this.outputNodes.push(node);", + " }", + " this.revision++;", + " }", + " getNode(id) {", + " return this.allNodes.get(id);", + " }", + " deleteNode(node) {", + " this.allNodes.delete(node.id);", + " this.revision++;", + " }", + " deleteWire(wire) {", + " let index = wire.input.outputs[wire.inputIndex].indexOf(wire);", + " if (index !== -1) {", + " wire.input.outputs[wire.inputIndex].splice(index, 1);", + " }", + " wire.output.inputs[wire.outputIndex] = undefined;", + " index = this.wires.indexOf(wire);", + " if (index != -1) {", + " this.wires[index] = this.wires[this.wires.length - 1];", + " this.wires.pop();", + " }", + " this.revision++;", + " }", + " toJSON() {", + " throw new Error('not implemented');", + " }", + " clone() {", + " throw new Error('not implemented');", + " }", + "}", + "", + "new THREE.Vector3(0, 0, 1);", + "const tempQ = new THREE.Quaternion();", + "const tempV = new THREE.Vector3();", + "const tempV2 = new THREE.Vector3();", + "const PREWARM_FPS = 60;", + "const DEFAULT_GEOMETRY = new THREE.PlaneGeometry(1, 1, 1, 1);", + "class NodeVFX {", + " set time(time) {", + " this.emissionState.time = time;", + " }", + " get time() {", + " return this.emissionState.time;", + " }", + " get layers() {", + " return this.rendererSettings.layers;", + " }", + " get texture() {", + " return this.rendererSettings.material.map;", + " }", + " set texture(texture) {", + " this.rendererSettings.material.map = texture;", + " this.neededToUpdateRender = true;", + " }", + " get material() {", + " return this.rendererSettings.material;", + " }", + " set material(material) {", + " this.rendererSettings.material = material;", + " this.neededToUpdateRender = true;", + " }", + " get instancingGeometry() {", + " return this.rendererSettings.instancingGeometry;", + " }", + " set instancingGeometry(geometry) {", + " this.restart();", + " this.particles.length = 0;", + " this.rendererSettings.instancingGeometry = geometry;", + " this.neededToUpdateRender = true;", + " }", + " get renderMode() {", + " return this.rendererSettings.renderMode;", + " }", + " set renderMode(renderMode) {", + " if ((this.rendererSettings.renderMode != RenderMode.Trail && renderMode === RenderMode.Trail) ||", + " (this.rendererSettings.renderMode == RenderMode.Trail && renderMode !== RenderMode.Trail)) {", + " this.restart();", + " this.particles.length = 0;", + " }", + " if (this.rendererSettings.renderMode !== renderMode) {", + " switch (renderMode) {", + " case RenderMode.Trail:", + " this.rendererEmitterSettings = {", + " startLength: 30,", + " followLocalOrigin: false,", + " };", + " break;", + " case RenderMode.Mesh:", + " this.rendererEmitterSettings = {", + " geometry: new THREE.PlaneGeometry(1, 1),", + " };", + " break;", + " case RenderMode.BillBoard:", + " case RenderMode.VerticalBillBoard:", + " case RenderMode.HorizontalBillBoard:", + " case RenderMode.StretchedBillBoard:", + " this.rendererEmitterSettings = {};", + " break;", + " }", + " }", + " this.rendererSettings.renderMode = renderMode;", + " this.neededToUpdateRender = true;", + " }", + " get renderOrder() {", + " return this.rendererSettings.renderOrder;", + " }", + " set renderOrder(renderOrder) {", + " this.rendererSettings.renderOrder = renderOrder;", + " this.neededToUpdateRender = true;", + " }", + " get blending() {", + " return this.rendererSettings.material.blending;", + " }", + " set blending(blending) {", + " this.rendererSettings.material.blending = blending;", + " this.neededToUpdateRender = true;", + " }", + " constructor(parameters) {", + " var _a, _b, _c, _d, _e, _f, _g;", + " this.temp = new THREE.Vector3();", + " this.travelDistance = 0;", + " this.normalMatrix = new THREE.Matrix3();", + " this.speedFactor = 0;", + " this.autoDestroy = parameters.autoDestroy === undefined ? false : parameters.autoDestroy;", + " this.duration = (_a = parameters.duration) !== null && _a !== void 0 ? _a : 1;", + " this.looping = parameters.looping === undefined ? true : parameters.looping;", + " this.prewarm = parameters.prewarm === undefined ? false : parameters.prewarm;", + " this.worldSpace = (_b = parameters.worldSpace) !== null && _b !== void 0 ? _b : false;", + " this.rendererEmitterSettings = (_c = parameters.rendererEmitterSettings) !== null && _c !== void 0 ? _c : {};", + " this.emissionGraph = parameters.emissionGraph;", + " this.updateGraph = parameters.updateGraph;", + " this.interpreter = new Interpreter();", + " this.rendererSettings = {", + " instancingGeometry: (_d = parameters.instancingGeometry) !== null && _d !== void 0 ? _d : DEFAULT_GEOMETRY,", + " renderMode: (_e = parameters.renderMode) !== null && _e !== void 0 ? _e : RenderMode.BillBoard,", + " renderOrder: (_f = parameters.renderOrder) !== null && _f !== void 0 ? _f : 0,", + " material: parameters.material,", + " layers: (_g = parameters.layers) !== null && _g !== void 0 ? _g : new THREE.Layers(),", + " uTileCount: 1,", + " vTileCount: 1,", + " };", + " this.neededToUpdateRender = true;", + " this.particles = new Array();", + " this.emitter = new ParticleEmitter(this);", + " this.paused = false;", + " this.particleNum = 0;", + " this.emissionState = {", + " time: 0,", + " };", + " this.emitEnded = false;", + " this.markForDestroy = false;", + " this.prewarmed = false;", + " }", + " pause() {", + " this.paused = true;", + " }", + " play() {", + " this.paused = false;", + " }", + " spawn(emissionState, matrix) {", + " tempQ.setFromRotationMatrix(matrix);", + " const translation = tempV;", + " const quaternion = tempQ;", + " const scale = tempV2;", + " matrix.decompose(translation, quaternion, scale);", + " this.particleNum++;", + " while (this.particles.length < this.particleNum) {", + " this.particles.push(new NodeParticle());", + " }", + " const particle = this.particles[this.particleNum - 1];", + " particle.reset();", + " this.interpreter.run(this.updateGraph, { particle: particle, emissionState: this.emissionState });", + " if (this.rendererSettings.renderMode === RenderMode.Trail &&", + " this.rendererEmitterSettings.followLocalOrigin) {", + " const trail = particle;", + " trail.localPosition = new THREE.Vector3().copy(trail.position);", + " }", + " if (this.worldSpace) {", + " particle.position.applyMatrix4(matrix);", + " particle.size *= (Math.abs(scale.x) + Math.abs(scale.y) + Math.abs(scale.z)) / 3;", + " particle.velocity.multiply(scale).applyMatrix3(this.normalMatrix);", + " if (particle.rotation && particle.rotation instanceof THREE.Quaternion) {", + " particle.rotation.multiplyQuaternions(tempQ, particle.rotation);", + " }", + " }", + " }", + " endEmit() {", + " this.emitEnded = true;", + " if (this.autoDestroy) {", + " this.markForDestroy = true;", + " }", + " }", + " dispose() {", + " if (this._renderer)", + " this._renderer.deleteSystem(this);", + " this.emitter.dispose();", + " if (this.emitter.parent)", + " this.emitter.parent.remove(this.emitter);", + " }", + " restart() {", + " this.paused = false;", + " this.particleNum = 0;", + " this.emissionState.time = 0;", + " this.emitEnded = false;", + " this.markForDestroy = false;", + " this.prewarmed = false;", + " }", + " update(delta) {", + " if (this.paused)", + " return;", + " let currentParent = this.emitter;", + " while (currentParent.parent) {", + " currentParent = currentParent.parent;", + " }", + " if (currentParent.type !== 'Scene') {", + " this.dispose();", + " return;", + " }", + " if (this.emitEnded && this.particleNum === 0) {", + " if (this.markForDestroy && this.emitter.parent)", + " this.dispose();", + " return;", + " }", + " if (this.looping && this.prewarm && !this.prewarmed) {", + " this.prewarmed = true;", + " for (let i = 0; i < this.duration * PREWARM_FPS; i++) {", + " this.update(1.0 / PREWARM_FPS);", + " }", + " }", + " if (delta > 0.1) {", + " delta = 0.1;", + " }", + " if (this.neededToUpdateRender) {", + " if (this._renderer)", + " this._renderer.updateSystem(this);", + " this.neededToUpdateRender = false;", + " }", + " this.emit(delta, this.emissionState, this.emitter.matrixWorld);", + " const context = { particle: undefined, emissionState: this.emissionState, delta };", + " for (let i = 0; i < this.particleNum; i++) {", + " context.particle = this.particles[i];", + " this.interpreter.run(this.updateGraph, context);", + " }", + " for (let i = 0; i < this.particleNum; i++) {", + " if (this.rendererEmitterSettings.followLocalOrigin &&", + " this.particles[i].localPosition) {", + " this.particles[i].position.copy(this.particles[i].localPosition);", + " if (this.particles[i].parentMatrix) {", + " this.particles[i].position.applyMatrix4(this.particles[i].parentMatrix);", + " }", + " else {", + " this.particles[i].position.applyMatrix4(this.emitter.matrixWorld);", + " }", + " }", + " else {", + " this.particles[i].position.addScaledVector(this.particles[i].velocity, delta);", + " }", + " this.particles[i].age += delta;", + " }", + " if (this.rendererSettings.renderMode === RenderMode.Trail) {", + " for (let i = 0; i < this.particleNum; i++) {", + " const particle = this.particles[i];", + " particle.update();", + " }", + " }", + " for (let i = 0; i < this.particleNum; i++) {", + " const particle = this.particles[i];", + " if (particle.died && (!(particle instanceof TrailParticle) || particle.previous.length === 0)) {", + " this.particles[i] = this.particles[this.particleNum - 1];", + " this.particles[this.particleNum - 1] = particle;", + " this.particleNum--;", + " i--;", + " }", + " }", + " }", + " emit(delta, emissionState, emitterMatrix) {", + " if (emissionState.time > this.duration) {", + " if (this.looping) {", + " emissionState.time -= this.duration;", + " }", + " else {", + " if (!this.emitEnded) {", + " this.endEmit();", + " }", + " }", + " }", + " this.normalMatrix.getNormalMatrix(emitterMatrix);", + " const context = {", + " signal: () => {", + " this.spawn(emissionState, emitterMatrix);", + " },", + " emissionState,", + " delta,", + " };", + " if (!this.emitEnded) {", + " this.interpreter.run(this.emissionGraph, context);", + " }", + " if (this.previousWorldPos === undefined)", + " this.previousWorldPos = new THREE.Vector3();", + " this.emitter.getWorldPosition(this.previousWorldPos);", + " emissionState.time += delta;", + " }", + " toJSON(meta, options = {}) {", + " return {};", + " }", + " getRendererSettings() {", + " return this.rendererSettings;", + " }", + " clone() {", + " return this;", + " }", + "}", + "", + "gdjs.__particleEmmiter3DExtension = {", + " ParticleEmitter3DRenderer,", + " ParticleEmitterAdapter,", + " ParticleEmitterHelper,", + "", + " ApplyCollision,", + " ApplyForce,", + " ApplySequences,", + " AxisAngleGenerator,", + " BatchedParticleRenderer,", + " BatchedRenderer,", + " BehaviorFromJSON,", + " BehaviorTypes,", + " Bezier,", + " ChangeEmitDirection,", + " ColorGeneratorFromJSON,", + " ColorOverLife,", + " ColorRange,", + " ConeEmitter,", + " ConstantColor,", + " ConstantValue,", + " DonutEmitter,", + " EmitSubParticleSystem,", + " EmitterFromJSON,", + " EmitterMode,", + " EmitterShapes,", + " EulerGenerator,", + " ForceOverLife,", + " FrameOverLife,", + " GeneratorFromJSON,", + " Gradient,", + " GraphNodeType,", + " GravityForce,", + " GridEmitter,", + " Interpreter,", + " IntervalValue,", + " MeshSurfaceEmitter,", + " Node,", + " NodeGraph,", + " NodeType,", + " NodeTypes,", + " NodeValueType,", + " Noise,", + " OrbitOverLife,", + " ParticleEmitter,", + " ParticleSystem,", + " PiecewiseBezier,", + " PiecewiseFunction,", + " Plugins,", + " PointEmitter,", + " QuarksLoader,", + " RandomColor,", + " RandomColorBetweenGradient,", + " RandomQuatGenerator,", + " RecordState,", + " RenderMode,", + " Rotation3DOverLife,", + " RotationGeneratorFromJSON,", + " RotationOverLife,", + " SequencerFromJSON,", + " SizeOverLife,", + " SpeedOverLife,", + " SphereEmitter,", + " SpriteBatch,", + " SpriteParticle,", + " SubParticleEmitMode,", + " TextureSequencer,", + " TrailBatch,", + " TrailParticle,", + " TurbulenceField,", + " VFXBatch,", + " ValueGeneratorFromJSON,", + " WidthOverLength,", + " Wire,", + " genDefaultForNodeValueType,", + " getPhysicsResolver,", + " loadPlugin,", + " setPhysicsResolver,", + " unloadPlugin,", + "}" + ], + "parameterObjects": "", + "useStrict": true, + "eventsSheetExpanded": true + } + ], + "parameters": [], + "objectGroups": [] + }, + { + "fullName": "", + "functionType": "Action", + "name": "onScenePreEvents", + "sentence": "", + "events": [ + { + "type": "BuiltinCommonInstructions::JsCode", + "inlineCode": [ + "\r", + "// See doStepPostEvents\r", + "runtimeScene.__particleEmmiter3DExtension = runtimeScene.__particleEmmiter3DExtension || {};\r", + "runtimeScene.__particleEmmiter3DExtension.emittersStepped = 0;" + ], + "parameterObjects": "", + "useStrict": true, + "eventsSheetExpanded": true + } + ], + "parameters": [], + "objectGroups": [] + } + ], + "eventsFunctionsFolderStructure": { + "folderName": "__ROOT", + "children": [ + { + "functionName": "DefineHelperClasses" + }, + { + "functionName": "onScenePreEvents" + } + ] + }, + "eventsBasedBehaviors": [], + "eventsBasedObjects": [ + { + "areaMaxX": 64, + "areaMaxY": 64, + "areaMaxZ": 64, + "areaMinX": 0, + "areaMinY": 0, + "areaMinZ": 0, + "assetStoreTag": "3d particles", + "defaultName": "ParticleEmitter", + "description": "Display a large number of particles to create visual effects.", + "fullName": "3D particle emitter", + "helpPath": "", + "iconUrl": "", + "is3D": true, + "isUsingLegacyInstancesRenderer": true, + "name": "ParticleEmitter3D", + "previewIconUrl": "", + "objects": [ + { + "adaptCollisionMaskAutomatically": true, + "assetStoreId": "", + "name": "Particle", + "type": "Sprite", + "updateIfNotVisible": false, + "variables": [], + "effects": [], + "behaviors": [], + "animations": [ + { + "name": "Image", + "useMultipleDirections": false, + "directions": [ + { + "looping": false, + "timeBetweenFrames": 0.08, + "sprites": [] + } + ] + } + ] + } + ], + "objectsFolderStructure": { + "folderName": "__ROOT", + "children": [ + { + "objectName": "Particle" + } + ] + }, + "objectsGroups": [], + "layers": [ + { + "ambientLightColorB": 200, + "ambientLightColorG": 200, + "ambientLightColorR": 200, + "camera2DPlaneMaxDrawingDistance": 5000, + "camera3DFarPlaneDistance": 10000, + "camera3DFieldOfView": 45, + "camera3DNearPlaneDistance": 3, + "cameraType": "", + "followBaseLayerCamera": false, + "isLightingLayer": false, + "isLocked": false, + "name": "", + "renderingType": "", + "visibility": true, + "cameras": [ + { + "defaultSize": true, + "defaultViewport": true, + "height": 0, + "viewportBottom": 1, + "viewportLeft": 0, + "viewportRight": 1, + "viewportTop": 0, + "width": 0 + } + ], + "effects": [] + } + ], + "instances": [], + "editionSettings": [], + "eventsFunctions": [ + { + "fullName": "", + "functionType": "Action", + "name": "onCreated", + "sentence": "", + "events": [ + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [], + "actions": [ + { + "type": { + "value": "ParticleEmitter3D::DefineHelperClasses" + }, + "parameters": [ + "", + "" + ] + } + ] + }, + { + "type": "BuiltinCommonInstructions::JsCode", + "inlineCode": [ + "const {", + " ParticleEmitterAdapter,", + " ParticleEmitter3DRenderer,", + " ParticleEmitterHelper,", + "", + " ParticleSystem,", + " IntervalValue,", + " ConstantValue,", + " ConstantColor,", + " ColorOverLife,", + " SizeOverLife,", + " ApplyForce,", + " WidthOverLength,", + " Gradient,", + " PiecewiseBezier,", + " Bezier,", + " PointEmitter,", + " ConeEmitter,", + " RenderMode", + "} = gdjs.__particleEmmiter3DExtension;", + "", + "/** @type {gdjs.CustomRuntimeObject} */", + "const object = objects[0];", + "// Force the size of the object because it doesn't contain any instance.", + "object._innerArea = {", + " min: [-16, -16, -16],", + " max: [16, 16, 16],", + " };", + "const hitbox = new gdjs.Polygon();", + "hitbox.vertices = [[-16, -16], [16, -16], [16, 16], [-16, 16]];", + "object._untransformedHitBoxes = [hitbox];", + "object._unrotatedAABB.min[0] = -16;", + "object._unrotatedAABB.min[1] = -16;", + "object._unrotatedAABB.max[0] = 16;", + "object._unrotatedAABB.max[1] = 16;", + "object._updateUntransformedHitBoxes = () => {};", + "", + "// Here runtimeScene is the gdjs.CustomRuntimeObjectInstanceContainer inside the custom object.", + "const gameScene = object.getRuntimeScene();", + "const game = runtimeScene.getGame();", + "", + "const texture = game.getImageManager().getThreeTexture('');", + "", + "// Set the blending here because changes are not applied after the emitter creation.", + "const blendingString = object._getBlending();", + "const blending =", + " blendingString === \"Additive\" ? THREE.AdditiveBlending :", + " blendingString === \"Normal\" ? THREE.NormalBlending :", + " blendingString === \"Subtractive\" ? THREE.SubtractiveBlending :", + " blendingString === \"Multiply\" ? THREE.MultiplyBlending :", + " blendingString === \"None\" ? THREE.NoBlending :", + " THREE.AdditiveBlending;", + "", + "", + "// Build a configuration with the right kind of objects.", + "// These values are not important as they are overrided by the object properties values.", + "const muzzle = {", + " duration: 10,", + " looping: false,", + " startLife: new IntervalValue(1, 2),", + " startSpeed: new IntervalValue(100, 200),", + " startSize: new IntervalValue(20, 50),", + " startColor: new ConstantColor(new THREE.Vector4(1, 1, 1, 1)),", + " worldSpace: true,", + "", + " maxParticle: 5,", + " emissionOverTime: new ConstantValue(50),", + " emissionBursts: [{", + " time: 0,", + " count: new ConstantValue(1),", + " cycle: 1,", + " interval: 0.01,", + " probability: 1,", + " }],", + "", + " shape: new PointEmitter(),", + " material: new THREE.MeshBasicMaterial({", + " map: texture,", + " blending: blending,", + " transparent: true,", + " side: THREE.DoubleSide", + " }),", + " startTileIndex: 0,", + " uTileCount: 1,", + " vTileCount: 1,", + " renderOrder: 2,", + " renderMode: RenderMode.BillBoard", + "};", + "const particleSystem = new ParticleSystem(muzzle);", + "", + "const colorOverLife = new ColorOverLife(", + " new Gradient(", + " [", + " [new THREE.Vector3(1, 0, 0), 0],", + " [new THREE.Vector3(1, 0, 0), 1],", + " ],", + " [", + " [1, 0],", + " [1, 1],", + " ]", + " ));", + "particleSystem.addBehavior(colorOverLife);", + "const sizeOverLife = new SizeOverLife(new PiecewiseBezier([[new Bezier(1, 2 / 3, 1 / 3, 0), 0]]));", + "particleSystem.addBehavior(sizeOverLife);", + "const applyForce = new ApplyForce(new THREE.Vector3(0, 0, -1), new ConstantValue(0));", + "particleSystem.addBehavior(applyForce);", + "const widthOverLength = new WidthOverLength(new PiecewiseBezier([[new Bezier(1, 2 / 3, 1 / 3, 0), 0]]));", + "particleSystem.addBehavior(widthOverLength);", + "", + "particleSystem.emitter.name = object.getName() + object.getNameId();", + "", + "object.__particleEmitterAdapter = new ParticleEmitterAdapter(particleSystem, colorOverLife, sizeOverLife, applyForce, widthOverLength);", + "object.__particleSystem = particleSystem;", + "", + "// This is a hack that may break in future releases.", + "// Replace the group that would hold children objects by the emmiter.", + "const layer = object.getInstanceContainer().getLayer(object.getLayer());", + "const group = object.getRenderer()._threeGroup;", + "layer.getRenderer().remove3DRendererObject(group);", + "particleSystem.emitter.position.copy(group.position);", + "particleSystem.emitter.rotation.order = 'ZYX';", + "particleSystem.emitter.rotation.copy(group.rotation);", + "", + "const particleEmitter3DRenderer = new ParticleEmitter3DRenderer(object, object._instanceContainer, object.getInstanceContainer());", + "object._renderer = particleEmitter3DRenderer;", + "if (game.isInGameEdition && game.isInGameEdition()) {", + " const particleEmitterHelper = new ParticleEmitterHelper(object);", + " particleEmitter3DRenderer._threeGroup = particleEmitterHelper;", + " layer.getRenderer().add3DRendererObject(particleEmitterHelper);", + "}", + "else {", + " particleEmitter3DRenderer._threeGroup = particleSystem.emitter;", + " layer.getRenderer().add3DRendererObject(particleSystem.emitter);", + "}", + "", + "particleSystem.emitter.updateMatrixWorld(true);", + "", + "", + "// See doStepPostEvents", + "gameScene.__particleEmmiter3DExtension = gameScene.__particleEmmiter3DExtension || {};", + "gameScene.__particleEmmiter3DExtension.emittersCount = gameScene.__particleEmmiter3DExtension.emittersCount || 0;", + "gameScene.__particleEmmiter3DExtension.emittersCount++;", + "", + "" + ], + "parameterObjects": "Object", + "useStrict": true, + "eventsSheetExpanded": true + }, + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [], + "actions": [ + { + "type": { + "value": "ParticleEmitter3D::ParticleEmitter3D::UpdateFromProperties" + }, + "parameters": [ + "Object", + "" + ] + }, + { + "type": { + "value": "ParticleEmitter3D::ParticleEmitter3D::SetRotationCenter" + }, + "parameters": [ + "Object", + "0", + "0", + "0" + ] + } + ] + } + ], + "parameters": [ + { + "description": "Object", + "name": "Object", + "supplementaryInformation": "ParticleEmitter3D::ParticleEmitter3D", + "type": "object" + } + ], + "objectGroups": [] + }, + { + "fullName": "", + "functionType": "Action", + "name": "onDestroy", + "sentence": "", + "events": [ + { + "type": "BuiltinCommonInstructions::JsCode", + "inlineCode": [ + "/** @type {gdjs.CustomRuntimeObject} */", + "const object = objects[0];", + "// Here runtimeScene is the gdjs.CustomRuntimeObjectInstanceContainer inside the custom object.", + "const gameScene = object.getRuntimeScene();", + "", + "object.__particleSystem.dispose();", + "", + "// See doStepPostEvents", + "gameScene.__particleEmmiter3DExtension.emittersCount--;", + "", + "if (gameScene.__particleEmmiter3DExtension.emittersCount === 0) {", + " // Update batch system now because doStepPostEvents won't be called.", + " gameScene.__particleEmmiter3DExtension.layerNames = gameScene.__particleEmmiter3DExtension.layerNames || [];", + " const layerNames = gameScene.__particleEmmiter3DExtension.layerNames;", + " gameScene.getAllLayerNames(layerNames);", + " for (const layerName of layerNames) {", + " const layer = gameScene.getLayer(layerName);", + " if (layer.__particleEmmiter3DExtension) {", + " layer.__particleEmmiter3DExtension.batchSystem.update(layer.getElapsedTime() / 1000);", + " }", + " }", + "}" + ], + "parameterObjects": "Object", + "useStrict": true, + "eventsSheetExpanded": true + } + ], + "parameters": [ + { + "description": "Object", + "name": "Object", + "supplementaryInformation": "ParticleEmitter3D::ParticleEmitter3D", + "type": "object" + } + ], + "objectGroups": [] + }, + { + "fullName": "", + "functionType": "Action", + "name": "onHotReloading", + "sentence": "", + "events": [ + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [], + "actions": [ + { + "type": { + "value": "ParticleEmitter3D::ParticleEmitter3D::UpdateFromProperties" + }, + "parameters": [ + "Object", + "" + ] + } + ] + } + ], + "parameters": [ + { + "description": "Object", + "name": "Object", + "supplementaryInformation": "ParticleEmitter3D::ParticleEmitter3D", + "type": "object" + } + ], + "objectGroups": [] + }, + { + "description": "Update from properties.", + "fullName": "Update from properties", + "functionType": "Action", + "name": "UpdateFromProperties", + "private": true, + "sentence": "Update from properties of _PARAM0_", + "events": [ + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [], + "actions": [ + { + "type": { + "value": "ParticleEmitter3D::ParticleEmitter3D::SetZ" + }, + "parameters": [ + "Object", + "=", + "Object.Z()", + "" + ] + }, + { + "type": { + "value": "ParticleEmitter3D::ParticleEmitter3D::SetRotationX" + }, + "parameters": [ + "Object", + "=", + "Object.PropertyRotationX()", + "" + ] + }, + { + "type": { + "value": "ParticleEmitter3D::ParticleEmitter3D::SetRotationY" + }, + "parameters": [ + "Object", + "=", + "Object.PropertyRotationY()", + "" + ] + }, + { + "type": { + "value": "ParticleEmitter3D::ParticleEmitter3D::SetRenderMode" + }, + "parameters": [ + "Object", + "=", + "RenderMode", + "" + ] + }, + { + "type": { + "value": "ParticleEmitter3D::ParticleEmitter3D::SetTrailStartLengthMin" + }, + "parameters": [ + "Object", + "=", + "TrailStartLengthMin", + "" + ] + }, + { + "type": { + "value": "ParticleEmitter3D::ParticleEmitter3D::SetTrailStartLengthMax" + }, + "parameters": [ + "Object", + "=", + "TrailStartLengthMax", + "" + ] + }, + { + "type": { + "value": "ParticleEmitter3D::ParticleEmitter3D::SetTrailEndWidthRatio" + }, + "parameters": [ + "Object", + "=", + "TrailEndWidthRatio", + "" + ] + }, + { + "type": { + "value": "ParticleEmitter3D::ParticleEmitter3D::SetStartColor" + }, + "parameters": [ + "Object", + "=", + "Object.StartColor()", + "" + ] + }, + { + "type": { + "value": "ParticleEmitter3D::ParticleEmitter3D::SetEndColor" + }, + "parameters": [ + "Object", + "=", + "Object.EndColor()", + "" + ] + }, + { + "type": { + "value": "ParticleEmitter3D::ParticleEmitter3D::SetStartOpacity" + }, + "parameters": [ + "Object", + "=", + "Object.StartOpacity()", + "" + ] + }, + { + "type": { + "value": "ParticleEmitter3D::ParticleEmitter3D::SetEndOpacity" + }, + "parameters": [ + "Object", + "=", + "Object.EndOpacity()", + "" + ] + }, + { + "type": { + "value": "ParticleEmitter3D::ParticleEmitter3D::SetFlow" + }, + "parameters": [ + "Object", + "=", + "Object.Flow()", + "" + ] + }, + { + "type": { + "value": "ParticleEmitter3D::ParticleEmitter3D::SetStartSizeMin" + }, + "parameters": [ + "Object", + "=", + "Object.StartSizeMin()", + "" + ] + }, + { + "type": { + "value": "ParticleEmitter3D::ParticleEmitter3D::SetStartSizeMax" + }, + "parameters": [ + "Object", + "=", + "Object.StartSizeMax()", + "" + ] + }, + { + "type": { + "value": "ParticleEmitter3D::ParticleEmitter3D::SetEndScale" + }, + "parameters": [ + "Object", + "=", + "Object.EndScale()", + "" + ] + }, + { + "type": { + "value": "ParticleEmitter3D::ParticleEmitter3D::SetStartSpeedMin" + }, + "parameters": [ + "Object", + "=", + "Object.StartSpeedMin()", + "" + ] + }, + { + "type": { + "value": "ParticleEmitter3D::ParticleEmitter3D::SetStartSpeedMax" + }, + "parameters": [ + "Object", + "=", + "Object.StartSpeedMax()", + "" + ] + }, + { + "type": { + "value": "ParticleEmitter3D::ParticleEmitter3D::SetLifespanMin" + }, + "parameters": [ + "Object", + "=", + "Object.LifespanMin()", + "" + ] + }, + { + "type": { + "value": "ParticleEmitter3D::ParticleEmitter3D::SetLifespanMax" + }, + "parameters": [ + "Object", + "=", + "Object.LifespanMax()", + "" + ] + }, + { + "type": { + "value": "ParticleEmitter3D::ParticleEmitter3D::SetDuration" + }, + "parameters": [ + "Object", + "=", + "Object.Duration()", + "" + ] + }, + { + "type": { + "value": "ParticleEmitter3D::ParticleEmitter3D::SetSpayConeAngle" + }, + "parameters": [ + "Object", + "=", + "Object.SpayConeAngle()", + "" + ] + }, + { + "type": { + "value": "ParticleEmitter3D::ParticleEmitter3D::SetGravity" + }, + "parameters": [ + "Object", + "=", + "Object.Gravity()", + "" + ] + }, + { + "type": { + "value": "ParticleEmitter3D::ParticleEmitter3D::SetGravityTop" + }, + "parameters": [ + "Object", + "=", + "Object.GravityTop()", + "" + ] + }, + { + "type": { + "value": "ParticleEmitter3D::ParticleEmitter3D::UpdateImage" + }, + "parameters": [ + "Object", + "" + ] + } + ], + "events": [ + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [ + { + "type": { + "inverted": true, + "value": "BooleanVariable" + }, + "parameters": [ + "IsTrailFollowingLocalOrigin", + "True", + "" + ] + } + ], + "actions": [ + { + "type": { + "value": "ParticleEmitter3D::ParticleEmitter3D::SetIsTrailFollowingLocalOrigin" + }, + "parameters": [ + "Object", + "=", + "TrailStartLengthMax" + ] + } + ] + }, + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [ + { + "type": { + "value": "BooleanVariable" + }, + "parameters": [ + "IsTrailFollowingLocalOrigin", + "True", + "" + ] + } + ], + "actions": [ + { + "type": { + "value": "ParticleEmitter3D::ParticleEmitter3D::SetIsTrailFollowingLocalOrigin" + }, + "parameters": [ + "Object", + "yes", + "TrailStartLengthMax" + ] + } + ] + }, + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [ + { + "type": { + "inverted": true, + "value": "BooleanVariable" + }, + "parameters": [ + "AreParticlesRelative", + "True", + "" + ] + } + ], + "actions": [ + { + "type": { + "value": "ParticleEmitter3D::ParticleEmitter3D::SetAreParticlesRelative" + }, + "parameters": [ + "Object", + "no", + "" + ] + } + ] + }, + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [ + { + "type": { + "value": "BooleanVariable" + }, + "parameters": [ + "AreParticlesRelative", + "True", + "" + ] + } + ], + "actions": [ + { + "type": { + "value": "ParticleEmitter3D::ParticleEmitter3D::SetAreParticlesRelative" + }, + "parameters": [ + "Object", + "yes", + "" + ] + } + ] + }, + { + "type": "BuiltinCommonInstructions::Comment", + "color": { + "b": 109, + "g": 230, + "r": 255, + "textB": 0, + "textG": 0, + "textR": 0 + }, + "comment": "TODO: Blending can't be changed." + }, + { + "disabled": true, + "type": "BuiltinCommonInstructions::Standard", + "conditions": [], + "actions": [ + { + "type": { + "value": "ParticleEmitter3D::ParticleEmitter3D::SetBlending" + }, + "parameters": [ + "Object", + "=", + "Object.Blending()", + "" + ] + } + ] + } + ] + }, + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [], + "actions": [ + { + "type": { + "value": "ParticleEmitter3D::ParticleEmitter3D::UpdateHelper" + }, + "parameters": [ + "Object", + "" + ] + } + ] + } + ], + "parameters": [ + { + "description": "Object", + "name": "Object", + "supplementaryInformation": "ParticleEmitter3D::ParticleEmitter3D", + "type": "object" + } + ], + "objectGroups": [] + }, + { + "fullName": "Update helper", + "functionType": "Action", + "name": "UpdateHelper", + "private": true, + "sentence": "Update graphical helper of _PARAM0_", + "events": [ + { + "type": "BuiltinCommonInstructions::JsCode", + "inlineCode": [ + "const game = runtimeScene.getGame();", + "if (game.isInGameEdition && game.isInGameEdition()) {", + " const particleEmitterHelper = objects[0].get3DRendererObject();", + " particleEmitterHelper.update();", + "}" + ], + "parameterObjects": "Object", + "useStrict": true, + "eventsSheetExpanded": false + } + ], + "parameters": [ + { + "description": "Object", + "name": "Object", + "supplementaryInformation": "ParticleEmitter3D::ParticleEmitter3D", + "type": "object" + } + ], + "objectGroups": [] + }, + { + "fullName": "Update particle image", + "functionType": "Action", + "name": "UpdateImage", + "private": true, + "sentence": "Update particle image of _PARAM0_", + "events": [ + { + "type": "BuiltinCommonInstructions::JsCode", + "inlineCode": [ + "/** @type {gdjs.CustomRuntimeObject3D} */\r", + "const object = objects[0];\r", + "/** @type {SpriteObjectDataType} */\r", + "const particleSpriteData = object._instanceContainer._objects.get(\"Particle\");\r", + "/** @type {gdjs.SpriteAnimationData} */\r", + "const animation = particleSpriteData.animations[0];\r", + "const animationFrame = animation ? animation.directions[0].sprites[0] : null;\r", + "const resourceName = animationFrame ? animationFrame.image : '';\r", + "object.__particleEmitterAdapter.setImage(resourceName);" + ], + "parameterObjects": "Object", + "useStrict": true, + "eventsSheetExpanded": true + } + ], + "parameters": [ + { + "description": "Object", + "name": "Object", + "supplementaryInformation": "ParticleEmitter3D::ParticleEmitter3D", + "type": "object" + } + ], + "objectGroups": [] + }, + { + "fullName": "", + "functionType": "Action", + "name": "doStepPostEvents", + "sentence": "", + "events": [ + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [ + { + "type": { + "value": "ParticleEmitter3D::ParticleEmitter3D::HasEnded" + }, + "parameters": [ + "Object", + "" + ] + } + ], + "actions": [ + { + "type": { + "value": "ParticleEmitter3D::ParticleEmitter3D::Delete" + }, + "parameters": [ + "Object", + "" + ] + } + ] + }, + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [ + { + "type": { + "inverted": true, + "value": "ParticleEmitter3D::ParticleEmitter3D::HasEnded" + }, + "parameters": [ + "Object", + "" + ] + } + ], + "actions": [ + { + "type": { + "value": "ParticleEmitter3D::ParticleEmitter3D::RegisterInLayer" + }, + "parameters": [ + "Object", + "" + ] + } + ], + "events": [ + { + "type": "BuiltinCommonInstructions::JsCode", + "inlineCode": [ + "/** @type {gdjs.CustomRuntimeObject3D} */", + "const object = objects[0];", + "// Here runtimeScene is the gdjs.CustomRuntimeObjectInstanceContainer inside the custom object.", + "const gameScene = object.getRuntimeScene();", + "", + "// Update batch system after having moved every emitter.", + "// See onScenePreEvents", + "gameScene.__particleEmmiter3DExtension.emittersStepped++;", + "if (gameScene.__particleEmmiter3DExtension.emittersStepped === gameScene.__particleEmmiter3DExtension.emittersCount) {", + " gameScene.__particleEmmiter3DExtension.layerNames = gameScene.__particleEmmiter3DExtension.layerNames || [];", + " const layerNames = gameScene.__particleEmmiter3DExtension.layerNames;", + " gameScene.getAllLayerNames(layerNames);", + " for (const layerName of layerNames) {", + " const layer = gameScene.getLayer(layerName);", + " if (layer.__particleEmmiter3DExtension) {", + " const timeDelta = layer.getElapsedTime() / 1000;", + " if (timeDelta > 0) {", + " layer.__particleEmmiter3DExtension.batchSystem.update(timeDelta);", + " }", + " }", + " }", + "}" + ], + "parameterObjects": "Object", + "useStrict": true, + "eventsSheetExpanded": true + } + ] + } + ], + "parameters": [ + { + "description": "Object", + "name": "Object", + "supplementaryInformation": "ParticleEmitter3D::ParticleEmitter3D", + "type": "object" + } + ], + "objectGroups": [] + }, + { + "fullName": "Register in layer", + "functionType": "Action", + "name": "RegisterInLayer", + "private": true, + "sentence": "Register _PARAM0_ in layer", + "events": [ + { + "type": "BuiltinCommonInstructions::JsCode", + "inlineCode": [ + "/** @type {gdjs.CustomRuntimeObject} */", + "const object = objects[0];", + "", + "// TODO Handle layer changes?", + "if (object.__emitterLayerName === undefined) {", + " // Here runtimeScene is the gdjs.CustomRuntimeObjectInstanceContainer inside the custom object.", + " const gameScene = object.getRuntimeScene();", + "", + " let rootObject = object;", + " while (rootObject.getInstanceContainer().getOwner) {", + " rootObject = rootObject.getInstanceContainer().getOwner();", + " }", + "", + " const layer = gameScene.getLayer(rootObject.getLayer());", + " layer.__particleEmmiter3DExtension = layer.__particleEmmiter3DExtension || {};", + " if (!layer.__particleEmmiter3DExtension.batchSystem) {", + " const batchSystem = new gdjs.__particleEmmiter3DExtension.BatchedRenderer();", + " batchSystem.scale.y *= -1;", + " const threeScene = layer.getRenderer().getThreeScene();", + " if (threeScene) {", + " threeScene.add(batchSystem);", + " }", + " layer.__particleEmmiter3DExtension.batchSystem = batchSystem;", + " }", + " layer.__particleEmmiter3DExtension.batchSystem.addSystem(object.__particleSystem);", + " object.__emitterLayerName = layer.getName();", + "}" + ], + "parameterObjects": "Object", + "useStrict": true, + "eventsSheetExpanded": true + } + ], + "parameters": [ + { + "description": "Object", + "name": "Object", + "supplementaryInformation": "ParticleEmitter3D::ParticleEmitter3D", + "type": "object" + } + ], + "objectGroups": [] + }, + { + "description": "Delete itself", + "fullName": "Delete itself", + "functionType": "Action", + "name": "Delete", + "private": true, + "sentence": "Delete _PARAM0_", + "events": [ + { + "type": "BuiltinCommonInstructions::JsCode", + "inlineCode": [ + "const object = objects[0];", + "", + "object.deleteFromScene(object.getParent());", + "" + ], + "parameterObjects": "Object", + "useStrict": true, + "eventsSheetExpanded": true + } + ], + "parameters": [ + { + "description": "Object", + "name": "Object", + "supplementaryInformation": "ParticleEmitter3D::ParticleEmitter3D", + "type": "object" + } + ], + "objectGroups": [] + }, + { + "description": "Check that emission has ended and no particle is alive anymore.", + "fullName": "Emission has ended", + "functionType": "Condition", + "name": "HasEnded", + "sentence": "Emission from _PARAM0_ has ended", + "events": [ + { + "type": "BuiltinCommonInstructions::JsCode", + "inlineCode": [ + "const object = objects[0];\r", + "\r", + "eventsFunctionContext.returnValue =\r", + " object._getShouldAutodestruct()\r", + " && object.__particleSystem.emitEnded\r", + " && object.__particleSystem.particleNum === 0;\r", + "" + ], + "parameterObjects": "Object", + "useStrict": true, + "eventsSheetExpanded": true + } + ], + "parameters": [ + { + "description": "Object", + "name": "Object", + "supplementaryInformation": "ParticleEmitter3D::ParticleEmitter3D", + "type": "object" + } + ], + "objectGroups": [] + }, + { + "description": "Restart particule emission from the beginning.", + "fullName": "Restart", + "functionType": "Action", + "name": "Restart", + "sentence": "Restart particule emission from _PARAM0_", + "events": [ + { + "type": "BuiltinCommonInstructions::JsCode", + "inlineCode": [ + "/** @type {gdjs.CustomRuntimeObject} */\r", + "const object = objects[0];\r", + "\r", + "object.__particleSystem.restart();\r", + "" + ], + "parameterObjects": "Object", + "useStrict": true, + "eventsSheetExpanded": false + } + ], + "parameters": [ + { + "description": "Object", + "name": "Object", + "supplementaryInformation": "ParticleEmitter3D::ParticleEmitter3D", + "type": "object" + } + ], + "objectGroups": [] + }, + { + "description": "the Z position of the emitter.", + "fullName": "Z elevaltion (deprecated)", + "functionType": "ExpressionAndCondition", + "group": "Position", + "name": "Z", + "private": true, + "sentence": "the Z position", + "events": [ + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [], + "actions": [ + { + "type": { + "value": "SetReturnNumber" + }, + "parameters": [ + "Object.PropertyZ()" + ] + } + ] + } + ], + "expressionType": { + "type": "expression" + }, + "parameters": [ + { + "description": "Object", + "name": "Object", + "supplementaryInformation": "ParticleEmitter3D::ParticleEmitter3D", + "type": "object" + } + ], + "objectGroups": [] + }, + { + "fullName": "", + "functionType": "ActionWithOperator", + "getterName": "Z", + "group": "Position", + "name": "SetZ", + "private": true, + "sentence": "", + "events": [ + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [], + "actions": [ + { + "type": { + "value": "SetNumberVariable" + }, + "parameters": [ + "Z", + "=", + "Value" + ] + } + ] + } + ], + "parameters": [ + { + "description": "Object", + "name": "Object", + "supplementaryInformation": "ParticleEmitter3D::ParticleEmitter3D", + "type": "object" + } + ], + "objectGroups": [] + }, + { + "description": "the rotation on X axis of the emitter.", + "fullName": "Rotation on X axis (deprecated)", + "functionType": "ExpressionAndCondition", + "group": "Angle", + "name": "RotationX", + "private": true, + "sentence": "the rotation on X axis", + "events": [ + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [], + "actions": [ + { + "type": { + "value": "SetReturnNumber" + }, + "parameters": [ + "Object.PropertyRotationX()" + ] + } + ] + } + ], + "expressionType": { + "type": "expression" + }, + "parameters": [ + { + "description": "Object", + "name": "Object", + "supplementaryInformation": "ParticleEmitter3D::ParticleEmitter3D", + "type": "object" + } + ], + "objectGroups": [] + }, + { + "fullName": "", + "functionType": "ActionWithOperator", + "getterName": "RotationX", + "group": "Angle", + "name": "SetRotationX", + "private": true, + "sentence": "", + "events": [ + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [], + "actions": [ + { + "type": { + "value": "SetNumberVariable" + }, + "parameters": [ + "RotationX", + "=", + "Value" + ] + } + ] + }, + { + "type": "BuiltinCommonInstructions::JsCode", + "inlineCode": [ + "const object = objects[0];\r", + "\r", + "object.__particleSystem.emitter.rotation.x = eventsFunctionContext.getArgument(\"Value\") * Math.PI / 180;" + ], + "parameterObjects": "Object", + "useStrict": true, + "eventsSheetExpanded": false + } + ], + "parameters": [ + { + "description": "Object", + "name": "Object", + "supplementaryInformation": "ParticleEmitter3D::ParticleEmitter3D", + "type": "object" + } + ], + "objectGroups": [] + }, + { + "description": "the rotation on Y axis of the emitter.", + "fullName": "Rotation on Y axis (deprecated)", + "functionType": "ExpressionAndCondition", + "group": "Angle", + "name": "RotationY", + "private": true, + "sentence": "the rotation on Y axis", + "events": [ + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [], + "actions": [ + { + "type": { + "value": "SetReturnNumber" + }, + "parameters": [ + "Object.PropertyRotationY()" + ] + } + ] + } + ], + "expressionType": { + "type": "expression" + }, + "parameters": [ + { + "description": "Object", + "name": "Object", + "supplementaryInformation": "ParticleEmitter3D::ParticleEmitter3D", + "type": "object" + } + ], + "objectGroups": [] + }, + { + "fullName": "", + "functionType": "ActionWithOperator", + "getterName": "RotationY", + "group": "Angle", + "name": "SetRotationY", + "private": true, + "sentence": "", + "events": [ + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [], + "actions": [ + { + "type": { + "value": "SetNumberVariable" + }, + "parameters": [ + "RotationY", + "=", + "Value" + ] + } + ] + }, + { + "type": "BuiltinCommonInstructions::JsCode", + "inlineCode": [ + "const object = objects[0];\r", + "\r", + "object.__particleSystem.emitter.rotation.y = (90 + eventsFunctionContext.getArgument(\"Value\")) * Math.PI / 180;" + ], + "parameterObjects": "Object", + "useStrict": true, + "eventsSheetExpanded": false + } + ], + "parameters": [ + { + "description": "Object", + "name": "Object", + "supplementaryInformation": "ParticleEmitter3D::ParticleEmitter3D", + "type": "object" + } + ], + "objectGroups": [] + }, + { + "description": "the start color of the object.", + "fullName": "Start color", + "functionType": "ExpressionAndCondition", + "group": "3D particle emitter color configuration", + "name": "StartColor", + "sentence": "the start color", + "events": [ + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [], + "actions": [ + { + "type": { + "value": "SetReturnString" + }, + "parameters": [ + "StartColor" + ] + } + ] + } + ], + "expressionType": { + "type": "color" + }, + "parameters": [ + { + "description": "Object", + "name": "Object", + "supplementaryInformation": "ParticleEmitter3D::ParticleEmitter3D", + "type": "object" + } + ], + "objectGroups": [] + }, + { + "fullName": "", + "functionType": "ActionWithOperator", + "getterName": "StartColor", + "group": "3D particle emitter color configuration", + "name": "SetStartColor", + "sentence": "", + "events": [ + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [], + "actions": [ + { + "type": { + "value": "SetStringVariable" + }, + "parameters": [ + "StartColor", + "=", + "Value" + ] + } + ] + }, + { + "type": "BuiltinCommonInstructions::JsCode", + "inlineCode": [ + "const object = objects[0];\r", + "const startColor = eventsFunctionContext.getArgument(\"Value\");\r", + "\r", + "object.__particleEmitterAdapter.setStartColor(startColor);" + ], + "parameterObjects": "Object", + "useStrict": true, + "eventsSheetExpanded": false + } + ], + "parameters": [ + { + "description": "Object", + "name": "Object", + "supplementaryInformation": "ParticleEmitter3D::ParticleEmitter3D", + "type": "object" + } + ], + "objectGroups": [] + }, + { + "description": "the end color of the object.", + "fullName": "End color", + "functionType": "ExpressionAndCondition", + "group": "3D particle emitter color configuration", + "name": "EndColor", + "sentence": "the end color", + "events": [ + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [], + "actions": [ + { + "type": { + "value": "SetReturnString" + }, + "parameters": [ + "EndColor" + ] + } + ] + } + ], + "expressionType": { + "type": "color" + }, + "parameters": [ + { + "description": "Object", + "name": "Object", + "supplementaryInformation": "ParticleEmitter3D::ParticleEmitter3D", + "type": "object" + } + ], + "objectGroups": [] + }, + { + "fullName": "", + "functionType": "ActionWithOperator", + "getterName": "EndColor", + "group": "3D particle emitter color configuration", + "name": "SetEndColor", + "sentence": "", + "events": [ + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [], + "actions": [ + { + "type": { + "value": "SetStringVariable" + }, + "parameters": [ + "EndColor", + "=", + "Value" + ] + } + ] + }, + { + "type": "BuiltinCommonInstructions::JsCode", + "inlineCode": [ + "const object = objects[0];\r", + "const endColor = eventsFunctionContext.getArgument(\"Value\");\r", + "\r", + "object.__particleEmitterAdapter.setEndColor(endColor);" + ], + "parameterObjects": "Object", + "useStrict": true, + "eventsSheetExpanded": false + } + ], + "parameters": [ + { + "description": "Object", + "name": "Object", + "supplementaryInformation": "ParticleEmitter3D::ParticleEmitter3D", + "type": "object" + } + ], + "objectGroups": [] + }, + { + "description": "the start opacity of the object.", + "fullName": "Start opacity", + "functionType": "ExpressionAndCondition", + "group": "3D particle emitter color configuration", + "name": "StartOpacity", + "sentence": "the start opacity", + "events": [ + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [], + "actions": [ + { + "type": { + "value": "SetReturnNumber" + }, + "parameters": [ + "StartOpacity" + ] + } + ] + } + ], + "expressionType": { + "type": "expression" + }, + "parameters": [ + { + "description": "Object", + "name": "Object", + "supplementaryInformation": "ParticleEmitter3D::ParticleEmitter3D", + "type": "object" + } + ], + "objectGroups": [] + }, + { + "fullName": "", + "functionType": "ActionWithOperator", + "getterName": "StartOpacity", + "group": "3D particle emitter color configuration", + "name": "SetStartOpacity", + "sentence": "", + "events": [ + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [], + "actions": [ + { + "type": { + "value": "SetNumberVariable" + }, + "parameters": [ + "StartOpacity", + "=", + "Value" + ] + } + ] + }, + { + "type": "BuiltinCommonInstructions::JsCode", + "inlineCode": [ + "const object = objects[0];\r", + "const startOpacity = eventsFunctionContext.getArgument(\"Value\");\r", + "\r", + "object.__particleEmitterAdapter.setStartOpacity(startOpacity);" + ], + "parameterObjects": "Object", + "useStrict": true, + "eventsSheetExpanded": false + } + ], + "parameters": [ + { + "description": "Object", + "name": "Object", + "supplementaryInformation": "ParticleEmitter3D::ParticleEmitter3D", + "type": "object" + } + ], + "objectGroups": [] + }, + { + "description": "the end opacity of the object.", + "fullName": "End opacity", + "functionType": "ExpressionAndCondition", + "group": "3D particle emitter color configuration", + "name": "EndOpacity", + "sentence": "the end opacity", + "events": [ + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [], + "actions": [ + { + "type": { + "value": "SetReturnNumber" + }, + "parameters": [ + "EndOpacity" + ] + } + ] + } + ], + "expressionType": { + "type": "expression" + }, + "parameters": [ + { + "description": "Object", + "name": "Object", + "supplementaryInformation": "ParticleEmitter3D::ParticleEmitter3D", + "type": "object" + } + ], + "objectGroups": [] + }, + { + "fullName": "", + "functionType": "ActionWithOperator", + "getterName": "EndOpacity", + "group": "3D particle emitter color configuration", + "name": "SetEndOpacity", + "sentence": "", + "events": [ + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [], + "actions": [ + { + "type": { + "value": "SetNumberVariable" + }, + "parameters": [ + "EndOpacity", + "=", + "Value" + ] + } + ] + }, + { + "type": "BuiltinCommonInstructions::JsCode", + "inlineCode": [ + "const object = objects[0];\r", + "const endOpacity = eventsFunctionContext.getArgument(\"Value\");\r", + "\r", + "object.__particleEmitterAdapter.setEndOpacity(endOpacity);" + ], + "parameterObjects": "Object", + "useStrict": true, + "eventsSheetExpanded": false + } + ], + "parameters": [ + { + "description": "Object", + "name": "Object", + "supplementaryInformation": "ParticleEmitter3D::ParticleEmitter3D", + "type": "object" + } + ], + "objectGroups": [] + }, + { + "description": "the flow of particles of the object (particles per second).", + "fullName": "Flow of particles", + "functionType": "ExpressionAndCondition", + "group": "3D particle emitter configuration", + "name": "Flow", + "sentence": "the flow of particles", + "events": [ + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [], + "actions": [ + { + "type": { + "value": "SetReturnNumber" + }, + "parameters": [ + "Flow" + ] + } + ] + } + ], + "expressionType": { + "type": "expression" + }, + "parameters": [ + { + "description": "Object", + "name": "Object", + "supplementaryInformation": "ParticleEmitter3D::ParticleEmitter3D", + "type": "object" + } + ], + "objectGroups": [] + }, + { + "fullName": "", + "functionType": "ActionWithOperator", + "getterName": "Flow", + "group": "3D particle emitter configuration", + "name": "SetFlow", + "sentence": "", + "events": [ + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [], + "actions": [ + { + "type": { + "value": "SetNumberVariable" + }, + "parameters": [ + "Flow", + "=", + "Value" + ] + } + ] + }, + { + "type": "BuiltinCommonInstructions::JsCode", + "inlineCode": [ + "const object = objects[0];\r", + "const flow = eventsFunctionContext.getArgument(\"Value\");\r", + "\r", + "object.__particleEmitterAdapter.setFlow(flow);\r", + "" + ], + "parameterObjects": "Object", + "useStrict": true, + "eventsSheetExpanded": false + } + ], + "parameters": [ + { + "description": "Object", + "name": "Object", + "supplementaryInformation": "ParticleEmitter3D::ParticleEmitter3D", + "type": "object" + } + ], + "objectGroups": [] + }, + { + "description": "the start min size of the object.", + "fullName": "Start min size", + "functionType": "ExpressionAndCondition", + "group": "3D particle emitter configuration", + "name": "StartSizeMin", + "sentence": "the start min size", + "events": [ + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [], + "actions": [ + { + "type": { + "value": "SetReturnNumber" + }, + "parameters": [ + "StartSizeMin" + ] + } + ] + } + ], + "expressionType": { + "type": "expression" + }, + "parameters": [ + { + "description": "Object", + "name": "Object", + "supplementaryInformation": "ParticleEmitter3D::ParticleEmitter3D", + "type": "object" + } + ], + "objectGroups": [] + }, + { + "fullName": "", + "functionType": "ActionWithOperator", + "getterName": "StartSizeMin", + "group": "3D particle emitter configuration", + "name": "SetStartSizeMin", + "sentence": "", + "events": [ + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [], + "actions": [ + { + "type": { + "value": "SetNumberVariable" + }, + "parameters": [ + "StartSizeMin", + "=", + "Value" + ] + } + ] + }, + { + "type": "BuiltinCommonInstructions::JsCode", + "inlineCode": [ + "const object = objects[0];\r", + "const startMinSize = eventsFunctionContext.getArgument(\"Value\");\r", + "\r", + "object.__particleEmitterAdapter.setStartMinSize(startMinSize);" + ], + "parameterObjects": "Object", + "useStrict": true, + "eventsSheetExpanded": false + } + ], + "parameters": [ + { + "description": "Object", + "name": "Object", + "supplementaryInformation": "ParticleEmitter3D::ParticleEmitter3D", + "type": "object" + } + ], + "objectGroups": [] + }, + { + "description": "the start max size of the object.", + "fullName": "Start max size", + "functionType": "ExpressionAndCondition", + "group": "3D particle emitter configuration", + "name": "StartSizeMax", + "sentence": "the start max size", + "events": [ + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [], + "actions": [ + { + "type": { + "value": "SetReturnNumber" + }, + "parameters": [ + "StartSizeMax" + ] + } + ] + } + ], + "expressionType": { + "type": "expression" + }, + "parameters": [ + { + "description": "Object", + "name": "Object", + "supplementaryInformation": "ParticleEmitter3D::ParticleEmitter3D", + "type": "object" + } + ], + "objectGroups": [] + }, + { + "fullName": "", + "functionType": "ActionWithOperator", + "getterName": "StartSizeMax", + "group": "3D particle emitter configuration", + "name": "SetStartSizeMax", + "sentence": "", + "events": [ + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [], + "actions": [ + { + "type": { + "value": "SetNumberVariable" + }, + "parameters": [ + "StartSizeMax", + "=", + "Value" + ] + } + ] + }, + { + "type": "BuiltinCommonInstructions::JsCode", + "inlineCode": [ + "const object = objects[0];\r", + "const startMaxSize = eventsFunctionContext.getArgument(\"Value\");\r", + "\r", + "object.__particleEmitterAdapter.setStartMaxSize(startMaxSize);\r", + "" + ], + "parameterObjects": "Object", + "useStrict": true, + "eventsSheetExpanded": false + } + ], + "parameters": [ + { + "description": "Object", + "name": "Object", + "supplementaryInformation": "ParticleEmitter3D::ParticleEmitter3D", + "type": "object" + } + ], + "objectGroups": [] + }, + { + "description": "the end scale of the object.", + "fullName": "End scale", + "functionType": "ExpressionAndCondition", + "group": "3D particle emitter configuration", + "name": "EndScale", + "sentence": "the end scale", + "events": [ + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [], + "actions": [ + { + "type": { + "value": "SetReturnNumber" + }, + "parameters": [ + "EndScale" + ] + } + ] + } + ], + "expressionType": { + "type": "expression" + }, + "parameters": [ + { + "description": "Object", + "name": "Object", + "supplementaryInformation": "ParticleEmitter3D::ParticleEmitter3D", + "type": "object" + } + ], + "objectGroups": [] + }, + { + "fullName": "", + "functionType": "ActionWithOperator", + "getterName": "EndScale", + "group": "3D particle emitter configuration", + "name": "SetEndScale", + "sentence": "", + "events": [ + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [], + "actions": [ + { + "type": { + "value": "SetNumberVariable" + }, + "parameters": [ + "EndScale", + "=", + "Value" + ] + } + ] + }, + { + "type": "BuiltinCommonInstructions::JsCode", + "inlineCode": [ + "const object = objects[0];\r", + "const endScale = eventsFunctionContext.getArgument(\"Value\");\r", + "\r", + "object.__particleEmitterAdapter.setEndScale(endScale);\r", + "" + ], + "parameterObjects": "Object", + "useStrict": true, + "eventsSheetExpanded": false + } + ], + "parameters": [ + { + "description": "Object", + "name": "Object", + "supplementaryInformation": "ParticleEmitter3D::ParticleEmitter3D", + "type": "object" + } + ], + "objectGroups": [] + }, + { + "description": "the min start speed of the object.", + "fullName": "Min start speed", + "functionType": "ExpressionAndCondition", + "group": "3D particle emitter speed configuration", + "name": "StartSpeedMin", + "sentence": "the min start speed", + "events": [ + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [], + "actions": [ + { + "type": { + "value": "SetReturnNumber" + }, + "parameters": [ + "StartSpeedMin" + ] + } + ] + } + ], + "expressionType": { + "type": "expression" + }, + "parameters": [ + { + "description": "Object", + "name": "Object", + "supplementaryInformation": "ParticleEmitter3D::ParticleEmitter3D", + "type": "object" + } + ], + "objectGroups": [] + }, + { + "fullName": "", + "functionType": "ActionWithOperator", + "getterName": "StartSpeedMin", + "group": "3D particle emitter speed configuration", + "name": "SetStartSpeedMin", + "sentence": "", + "events": [ + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [], + "actions": [ + { + "type": { + "value": "SetNumberVariable" + }, + "parameters": [ + "StartSpeedMin", + "=", + "Value" + ] + } + ] + }, + { + "type": "BuiltinCommonInstructions::JsCode", + "inlineCode": [ + "const object = objects[0];\r", + "const startSpeedMin = eventsFunctionContext.getArgument(\"Value\");\r", + "\r", + "object.__particleEmitterAdapter.setStartSpeedMin(startSpeedMin);" + ], + "parameterObjects": "Object", + "useStrict": true, + "eventsSheetExpanded": false + } + ], + "parameters": [ + { + "description": "Object", + "name": "Object", + "supplementaryInformation": "ParticleEmitter3D::ParticleEmitter3D", + "type": "object" + } + ], + "objectGroups": [] + }, + { + "description": "the max start speed of the object.", + "fullName": "Max start speed", + "functionType": "ExpressionAndCondition", + "group": "3D particle emitter speed configuration", + "name": "StartSpeedMax", + "sentence": "the max start speed", + "events": [ + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [], + "actions": [ + { + "type": { + "value": "SetReturnNumber" + }, + "parameters": [ + "StartSpeedMax" + ] + } + ] + } + ], + "expressionType": { + "type": "expression" + }, + "parameters": [ + { + "description": "Object", + "name": "Object", + "supplementaryInformation": "ParticleEmitter3D::ParticleEmitter3D", + "type": "object" + } + ], + "objectGroups": [] + }, + { + "fullName": "", + "functionType": "ActionWithOperator", + "getterName": "StartSpeedMax", + "group": "3D particle emitter speed configuration", + "name": "SetStartSpeedMax", + "sentence": "", + "events": [ + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [], + "actions": [ + { + "type": { + "value": "SetNumberVariable" + }, + "parameters": [ + "StartSpeedMax", + "=", + "Value" + ] + } + ] + }, + { + "type": "BuiltinCommonInstructions::JsCode", + "inlineCode": [ + "const object = objects[0];\r", + "const startSpeedMax = eventsFunctionContext.getArgument(\"Value\");\r", + "\r", + "object.__particleEmitterAdapter.setStartSpeedMax(startSpeedMax);" + ], + "parameterObjects": "Object", + "useStrict": true, + "eventsSheetExpanded": false + } + ], + "parameters": [ + { + "description": "Object", + "name": "Object", + "supplementaryInformation": "ParticleEmitter3D::ParticleEmitter3D", + "type": "object" + } + ], + "objectGroups": [] + }, + { + "description": "the min lifespan of the object.", + "fullName": "Min lifespan", + "functionType": "ExpressionAndCondition", + "group": "3D particle emitter configuration", + "name": "LifespanMin", + "sentence": "the min lifespan", + "events": [ + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [], + "actions": [ + { + "type": { + "value": "SetReturnNumber" + }, + "parameters": [ + "LifespanMin" + ] + } + ] + } + ], + "expressionType": { + "type": "expression" + }, + "parameters": [ + { + "description": "Object", + "name": "Object", + "supplementaryInformation": "ParticleEmitter3D::ParticleEmitter3D", + "type": "object" + } + ], + "objectGroups": [] + }, + { + "fullName": "", + "functionType": "ActionWithOperator", + "getterName": "LifespanMin", + "group": "3D particle emitter configuration", + "name": "SetLifespanMin", + "sentence": "", + "events": [ + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [], + "actions": [ + { + "type": { + "value": "SetNumberVariable" + }, + "parameters": [ + "LifespanMin", + "=", + "Value" + ] + } + ] + }, + { + "type": "BuiltinCommonInstructions::JsCode", + "inlineCode": [ + "const object = objects[0];\r", + "const lifespanMin = eventsFunctionContext.getArgument(\"Value\");\r", + "\r", + "object.__particleEmitterAdapter.setLifespanMin(lifespanMin);\r", + "" + ], + "parameterObjects": "Object", + "useStrict": true, + "eventsSheetExpanded": false + } + ], + "parameters": [ + { + "description": "Object", + "name": "Object", + "supplementaryInformation": "ParticleEmitter3D::ParticleEmitter3D", + "type": "object" + } + ], + "objectGroups": [] + }, + { + "description": "the max lifespan of the object.", + "fullName": "Max lifespan", + "functionType": "ExpressionAndCondition", + "group": "3D particle emitter configuration", + "name": "LifespanMax", + "sentence": "the max lifespan", + "events": [ + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [], + "actions": [ + { + "type": { + "value": "SetReturnNumber" + }, + "parameters": [ + "LifespanMax" + ] + } + ] + } + ], + "expressionType": { + "type": "expression" + }, + "parameters": [ + { + "description": "Object", + "name": "Object", + "supplementaryInformation": "ParticleEmitter3D::ParticleEmitter3D", + "type": "object" + } + ], + "objectGroups": [] + }, + { + "fullName": "", + "functionType": "ActionWithOperator", + "getterName": "LifespanMax", + "group": "3D particle emitter configuration", + "name": "SetLifespanMax", + "sentence": "", + "events": [ + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [], + "actions": [ + { + "type": { + "value": "SetNumberVariable" + }, + "parameters": [ + "LifespanMax", + "=", + "Value" + ] + } + ] + }, + { + "type": "BuiltinCommonInstructions::JsCode", + "inlineCode": [ + "const object = objects[0];\r", + "const lifespanMax = eventsFunctionContext.getArgument(\"Value\");\r", + "\r", + "object.__particleEmitterAdapter.setLifespanMax(lifespanMax);\r", + "" + ], + "parameterObjects": "Object", + "useStrict": true, + "eventsSheetExpanded": false + } + ], + "parameters": [ + { + "description": "Object", + "name": "Object", + "supplementaryInformation": "ParticleEmitter3D::ParticleEmitter3D", + "type": "object" + } + ], + "objectGroups": [] + }, + { + "description": "the emission duration of the object.", + "fullName": "Emission duration", + "functionType": "ExpressionAndCondition", + "group": "3D particle emitter configuration", + "name": "Duration", + "sentence": "the emission duration", + "events": [ + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [], + "actions": [ + { + "type": { + "value": "SetReturnNumber" + }, + "parameters": [ + "Duration" + ] + } + ] + } + ], + "expressionType": { + "type": "expression" + }, + "parameters": [ + { + "description": "Object", + "name": "Object", + "supplementaryInformation": "ParticleEmitter3D::ParticleEmitter3D", + "type": "object" + } + ], + "objectGroups": [] + }, + { + "fullName": "", + "functionType": "ActionWithOperator", + "getterName": "Duration", + "group": "3D particle emitter configuration", + "name": "SetDuration", + "sentence": "", + "events": [ + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [], + "actions": [ + { + "type": { + "value": "SetNumberVariable" + }, + "parameters": [ + "Duration", + "=", + "Value" + ] + } + ] + }, + { + "type": "BuiltinCommonInstructions::JsCode", + "inlineCode": [ + "const object = objects[0];\r", + "const duration = eventsFunctionContext.getArgument(\"Value\");\r", + "\r", + "object.__particleEmitterAdapter.setDuration(duration);\r", + "" + ], + "parameterObjects": "Object", + "useStrict": true, + "eventsSheetExpanded": false + } + ], + "parameters": [ + { + "description": "Object", + "name": "Object", + "supplementaryInformation": "ParticleEmitter3D::ParticleEmitter3D", + "type": "object" + } + ], + "objectGroups": [] + }, + { + "description": "Check if particles move with the emitter.", + "fullName": "Particles move with the emitter", + "functionType": "Condition", + "group": "3D particle emitter speed configuration", + "name": "AreParticlesRelative", + "sentence": "_PARAM0_ particles move with the emitter", + "events": [ + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [ + { + "type": { + "value": "BooleanVariable" + }, + "parameters": [ + "AreParticlesRelative", + "True", + "" + ] + } + ], + "actions": [ + { + "type": { + "value": "SetReturnBoolean" + }, + "parameters": [ + "True" + ] + } + ] + } + ], + "parameters": [ + { + "description": "Object", + "name": "Object", + "supplementaryInformation": "ParticleEmitter3D::ParticleEmitter3D", + "type": "object" + } + ], + "objectGroups": [] + }, + { + "description": "Change if particles move with the emitter.", + "fullName": "Particles move with the emitter", + "functionType": "Action", + "group": "3D particle emitter speed configuration", + "name": "SetAreParticlesRelative", + "sentence": "_PARAM0_ particles move with the emitter: _PARAM1_", + "events": [ + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [ + { + "type": { + "inverted": true, + "value": "BooleanVariable" + }, + "parameters": [ + "Value", + "True", + "" + ] + } + ], + "actions": [ + { + "type": { + "value": "SetBooleanVariable" + }, + "parameters": [ + "AreParticlesRelative", + "False", + "" + ] + } + ] + }, + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [ + { + "type": { + "value": "BooleanVariable" + }, + "parameters": [ + "Value", + "True", + "" + ] + } + ], + "actions": [ + { + "type": { + "value": "SetBooleanVariable" + }, + "parameters": [ + "AreParticlesRelative", + "True", + "" + ] + } + ] + }, + { + "type": "BuiltinCommonInstructions::JsCode", + "inlineCode": [ + "const object = objects[0];\r", + "const areParticlesRelative = eventsFunctionContext.getArgument(\"Value\");\r", + "\r", + "object.__particleEmitterAdapter.setParticlesRelative(areParticlesRelative);\r", + "" + ], + "parameterObjects": "Object", + "useStrict": true, + "eventsSheetExpanded": false + } + ], + "parameters": [ + { + "description": "Object", + "name": "Object", + "supplementaryInformation": "ParticleEmitter3D::ParticleEmitter3D", + "type": "object" + }, + { + "defaultValue": "yes", + "description": "AreParticlesRelative", + "name": "Value", + "optional": true, + "type": "yesorno" + } + ], + "objectGroups": [] + }, + { + "description": "the spay cone angle of the object.", + "fullName": "Spay cone angle", + "functionType": "ExpressionAndCondition", + "group": "3D particle emitter configuration", + "name": "SpayConeAngle", + "sentence": "the spay cone angle", + "events": [ + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [], + "actions": [ + { + "type": { + "value": "SetReturnNumber" + }, + "parameters": [ + "SpayConeAngle" + ] + } + ] + } + ], + "expressionType": { + "type": "expression" + }, + "parameters": [ + { + "description": "Object", + "name": "Object", + "supplementaryInformation": "ParticleEmitter3D::ParticleEmitter3D", + "type": "object" + } + ], + "objectGroups": [] + }, + { + "fullName": "", + "functionType": "ActionWithOperator", + "getterName": "SpayConeAngle", + "group": "3D particle emitter configuration", + "name": "SetSpayConeAngle", + "sentence": "", + "events": [ + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [], + "actions": [ + { + "type": { + "value": "SetNumberVariable" + }, + "parameters": [ + "SpayConeAngle", + "=", + "Value" + ] + } + ] + }, + { + "type": "BuiltinCommonInstructions::JsCode", + "inlineCode": [ + "const object = objects[0];\r", + "const sprayConeAngle = eventsFunctionContext.getArgument(\"Value\");\r", + "\r", + "object.__particleEmitterAdapter.setSprayConeAngle(sprayConeAngle);\r", + "" + ], + "parameterObjects": "Object", + "useStrict": true, + "eventsSheetExpanded": true + } + ], + "parameters": [ + { + "description": "Object", + "name": "Object", + "supplementaryInformation": "ParticleEmitter3D::ParticleEmitter3D", + "type": "object" + } + ], + "objectGroups": [] + }, + { + "description": "the blending of the object.", + "fullName": "Blending", + "functionType": "ExpressionAndCondition", + "group": "3D particle emitter color configuration", + "name": "Blending", + "sentence": "the blending", + "events": [ + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [], + "actions": [ + { + "type": { + "value": "SetReturnString" + }, + "parameters": [ + "Blending" + ] + } + ] + } + ], + "expressionType": { + "supplementaryInformation": "[\"Normal\",\"Additive\",\"Substractive\",\"Multiply\",\"None\"]", + "type": "stringWithSelector" + }, + "parameters": [ + { + "description": "Object", + "name": "Object", + "supplementaryInformation": "ParticleEmitter3D::ParticleEmitter3D", + "type": "object" + } + ], + "objectGroups": [] + }, + { + "fullName": "", + "functionType": "ActionWithOperator", + "getterName": "Blending", + "group": "3D particle emitter color configuration", + "name": "SetBlending", + "private": true, + "sentence": "", + "events": [ + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [], + "actions": [ + { + "type": { + "value": "SetStringVariable" + }, + "parameters": [ + "Blending", + "=", + "Value" + ] + } + ] + }, + { + "type": "BuiltinCommonInstructions::JsCode", + "inlineCode": [ + "const object = objects[0];\r", + "const blending = eventsFunctionContext.getArgument(\"Value\");\r", + "\r", + "object.__particleEmitterAdapter.setBlending(blending);\r", + "" + ], + "parameterObjects": "Object", + "useStrict": true, + "eventsSheetExpanded": true + } + ], + "parameters": [ + { + "description": "Object", + "name": "Object", + "supplementaryInformation": "ParticleEmitter3D::ParticleEmitter3D", + "type": "object" + } + ], + "objectGroups": [] + }, + { + "description": "the gravity top of the object.", + "fullName": "Gravity top", + "functionType": "ExpressionAndCondition", + "group": "3D particle emitter speed configuration", + "name": "GravityTop", + "sentence": "the gravity top", + "events": [ + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [], + "actions": [ + { + "type": { + "value": "SetReturnString" + }, + "parameters": [ + "GravityTop" + ] + } + ] + } + ], + "expressionType": { + "supplementaryInformation": "[\"Y-\",\"Z+\"]", + "type": "stringWithSelector" + }, + "parameters": [ + { + "description": "Object", + "name": "Object", + "supplementaryInformation": "ParticleEmitter3D::ParticleEmitter3D", + "type": "object" + } + ], + "objectGroups": [] + }, + { + "fullName": "", + "functionType": "ActionWithOperator", + "getterName": "GravityTop", + "group": "3D particle emitter speed configuration", + "name": "SetGravityTop", + "sentence": "", + "events": [ + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [], + "actions": [ + { + "type": { + "value": "SetStringVariable" + }, + "parameters": [ + "GravityTop", + "=", + "Value" + ] + } + ] + }, + { + "type": "BuiltinCommonInstructions::JsCode", + "inlineCode": [ + "const object = objects[0];\r", + "const gravityTop = eventsFunctionContext.getArgument(\"Value\");\r", + "\r", + "object.__particleEmitterAdapter.setGravityTop(gravityTop);\r", + "" + ], + "parameterObjects": "Object", + "useStrict": true, + "eventsSheetExpanded": true + } + ], + "parameters": [ + { + "description": "Object", + "name": "Object", + "supplementaryInformation": "ParticleEmitter3D::ParticleEmitter3D", + "type": "object" + } + ], + "objectGroups": [] + }, + { + "description": "the gravity of the object.", + "fullName": "Gravity", + "functionType": "ExpressionAndCondition", + "group": "3D particle emitter speed configuration", + "name": "Gravity", + "sentence": "the gravity", + "events": [ + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [], + "actions": [ + { + "type": { + "value": "SetReturnNumber" + }, + "parameters": [ + "Gravity" + ] + } + ] + } + ], + "expressionType": { + "type": "expression" + }, + "parameters": [ + { + "description": "Object", + "name": "Object", + "supplementaryInformation": "ParticleEmitter3D::ParticleEmitter3D", + "type": "object" + } + ], + "objectGroups": [] + }, + { + "fullName": "", + "functionType": "ActionWithOperator", + "getterName": "Gravity", + "group": "3D particle emitter speed configuration", + "name": "SetGravity", + "sentence": "", + "events": [ + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [], + "actions": [ + { + "type": { + "value": "SetNumberVariable" + }, + "parameters": [ + "Gravity", + "=", + "Value" + ] + } + ] + }, + { + "type": "BuiltinCommonInstructions::JsCode", + "inlineCode": [ + "const object = objects[0];\r", + "const gravity = eventsFunctionContext.getArgument(\"Value\");\r", + "\r", + "object.__particleEmitterAdapter.setGravity(gravity);\r", + "" + ], + "parameterObjects": "Object", + "useStrict": true, + "eventsSheetExpanded": true + } + ], + "parameters": [ + { + "description": "Object", + "name": "Object", + "supplementaryInformation": "ParticleEmitter3D::ParticleEmitter3D", + "type": "object" + } + ], + "objectGroups": [] + }, + { + "description": "Check if delete when emission ends.", + "fullName": "Delete when emission ends", + "functionType": "Condition", + "group": "3D particle emitter configuration", + "name": "ShouldAutodestruct", + "sentence": "_PARAM0_ delete when emission ends", + "events": [ + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [ + { + "type": { + "value": "BooleanVariable" + }, + "parameters": [ + "ShouldAutodestruct", + "True", + "" + ] + } + ], + "actions": [ + { + "type": { + "value": "SetReturnBoolean" + }, + "parameters": [ + "True" + ] + } + ] + } + ], + "parameters": [ + { + "description": "Object", + "name": "Object", + "supplementaryInformation": "ParticleEmitter3D::ParticleEmitter3D", + "type": "object" + } + ], + "objectGroups": [] + }, + { + "description": "Change if delete when emission ends.", + "fullName": "Delete when emission ends", + "functionType": "Action", + "group": "3D particle emitter configuration", + "name": "SetShouldAutodestruct", + "sentence": "_PARAM0_ delete when emission ends: _PARAM1_", + "events": [ + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [ + { + "type": { + "inverted": true, + "value": "BooleanVariable" + }, + "parameters": [ + "Value", + "True", + "" + ] + } + ], + "actions": [ + { + "type": { + "value": "SetBooleanVariable" + }, + "parameters": [ + "ShouldAutodestruct", + "False", + "" + ] + } + ] + }, + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [ + { + "type": { + "value": "BooleanVariable" + }, + "parameters": [ + "Value", + "True", + "" + ] + } + ], + "actions": [ + { + "type": { + "value": "SetBooleanVariable" + }, + "parameters": [ + "ShouldAutodestruct", + "True", + "" + ] + } + ] + } + ], + "parameters": [ + { + "description": "Object", + "name": "Object", + "supplementaryInformation": "ParticleEmitter3D::ParticleEmitter3D", + "type": "object" + }, + { + "defaultValue": "yes", + "description": "ShouldAutodestruct", + "name": "Value", + "optional": true, + "type": "yesorno" + } + ], + "objectGroups": [] + }, + { + "description": "the start min trail length of the object.", + "fullName": "Start min trail length", + "functionType": "ExpressionAndCondition", + "group": "3D particle emitter trail configuration", + "name": "TrailStartLengthMin", + "sentence": "the start min trail length", + "events": [ + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [], + "actions": [ + { + "type": { + "value": "SetReturnNumber" + }, + "parameters": [ + "TrailStartLengthMin" + ] + } + ] + } + ], + "expressionType": { + "type": "expression" + }, + "parameters": [ + { + "description": "Object", + "name": "Object", + "supplementaryInformation": "ParticleEmitter3D::ParticleEmitter3D", + "type": "object" + } + ], + "objectGroups": [] + }, + { + "fullName": "", + "functionType": "ActionWithOperator", + "getterName": "TrailStartLengthMin", + "group": "3D particle emitter trail configuration", + "name": "SetTrailStartLengthMin", + "sentence": "", + "events": [ + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [], + "actions": [ + { + "type": { + "value": "SetNumberVariable" + }, + "parameters": [ + "TrailStartLengthMin", + "=", + "Value" + ] + } + ] + }, + { + "type": "BuiltinCommonInstructions::JsCode", + "inlineCode": [ + "const object = objects[0];\r", + "const value = eventsFunctionContext.getArgument(\"Value\");\r", + "\r", + "object.__particleEmitterAdapter.setTrailStartLengthMin(value);" + ], + "parameterObjects": "Object", + "useStrict": true, + "eventsSheetExpanded": false + } + ], + "parameters": [ + { + "description": "Object", + "name": "Object", + "supplementaryInformation": "ParticleEmitter3D::ParticleEmitter3D", + "type": "object" + } + ], + "objectGroups": [] + }, + { + "description": "the start max trail length of the object.", + "fullName": "Start max trail length", + "functionType": "ExpressionAndCondition", + "group": "3D particle emitter trail configuration", + "name": "TrailStartLengthMax", + "sentence": "the start max trail length", + "events": [ + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [], + "actions": [ + { + "type": { + "value": "SetReturnNumber" + }, + "parameters": [ + "TrailStartLengthMax" + ] + } + ] + } + ], + "expressionType": { + "type": "expression" + }, + "parameters": [ + { + "description": "Object", + "name": "Object", + "supplementaryInformation": "ParticleEmitter3D::ParticleEmitter3D", + "type": "object" + } + ], + "objectGroups": [] + }, + { + "fullName": "", + "functionType": "ActionWithOperator", + "getterName": "TrailStartLengthMax", + "group": "3D particle emitter trail configuration", + "name": "SetTrailStartLengthMax", + "sentence": "", + "events": [ + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [], + "actions": [ + { + "type": { + "value": "SetNumberVariable" + }, + "parameters": [ + "TrailStartLengthMax", + "=", + "Value" + ] + } + ] + }, + { + "type": "BuiltinCommonInstructions::JsCode", + "inlineCode": [ + "const object = objects[0];\r", + "const value = eventsFunctionContext.getArgument(\"Value\");\r", + "\r", + "object.__particleEmitterAdapter.setTrailStartLengthMax(value);" + ], + "parameterObjects": "Object", + "useStrict": true, + "eventsSheetExpanded": false + } + ], + "parameters": [ + { + "description": "Object", + "name": "Object", + "supplementaryInformation": "ParticleEmitter3D::ParticleEmitter3D", + "type": "object" + } + ], + "objectGroups": [] + }, + { + "description": "Check if the trail should follow the object.", + "fullName": "Trail following the object", + "functionType": "Condition", + "group": "3D particle emitter trail configuration", + "name": "IsTrailFollowingLocalOrigin", + "sentence": "The trail is following _PARAM0_ ", + "events": [ + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [ + { + "type": { + "value": "BooleanVariable" + }, + "parameters": [ + "IsTrailFollowingLocalOrigin", + "True", + "" + ] + } + ], + "actions": [ + { + "type": { + "value": "SetReturnBoolean" + }, + "parameters": [ + "True" + ] + } + ] + } + ], + "parameters": [ + { + "description": "Object", + "name": "Object", + "supplementaryInformation": "ParticleEmitter3D::ParticleEmitter3D", + "type": "object" + } + ], + "objectGroups": [] + }, + { + "description": "Change if the trail should follow the object or not.", + "fullName": "Make the trail follow", + "functionType": "Action", + "group": "3D particle emitter trail configuration", + "name": "SetIsTrailFollowingLocalOrigin", + "sentence": "Make the trail follow _PARAM0_: _PARAM1_", + "events": [ + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [ + { + "type": { + "value": "BooleanVariable" + }, + "parameters": [ + "Value", + "False", + "" + ] + } + ], + "actions": [ + { + "type": { + "value": "SetBooleanVariable" + }, + "parameters": [ + "IsTrailFollowingLocalOrigin", + "False", + "" + ] + } + ] + }, + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [ + { + "type": { + "value": "BooleanVariable" + }, + "parameters": [ + "Value", + "True", + "" + ] + } + ], + "actions": [ + { + "type": { + "value": "SetBooleanVariable" + }, + "parameters": [ + "IsTrailFollowingLocalOrigin", + "True", + "" + ] + } + ] + }, + { + "type": "BuiltinCommonInstructions::JsCode", + "inlineCode": [ + "const object = objects[0];\r", + "const value = eventsFunctionContext.getArgument(\"Value\");\r", + "\r", + "object.__particleEmitterAdapter.setTrailFollowingLocalOrigin(value);" + ], + "parameterObjects": "Object", + "useStrict": true, + "eventsSheetExpanded": false + } + ], + "parameters": [ + { + "description": "Object", + "name": "Object", + "supplementaryInformation": "ParticleEmitter3D::ParticleEmitter3D", + "type": "object" + }, + { + "defaultValue": "yes", + "description": "IsTrailFollowingLocalOrigin", + "name": "Value", + "optional": true, + "type": "yesorno" + } + ], + "objectGroups": [] + }, + { + "description": "the tail end width ratio of the object.", + "fullName": "Tail end width ratio", + "functionType": "ExpressionAndCondition", + "group": "3D particle emitter trail configuration", + "name": "TrailEndWidthRatio", + "sentence": "the tail end width ratio", + "events": [ + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [], + "actions": [ + { + "type": { + "value": "SetReturnNumber" + }, + "parameters": [ + "TrailEndWidthRatio" + ] + } + ] + } + ], + "expressionType": { + "type": "expression" + }, + "parameters": [ + { + "description": "Object", + "name": "Object", + "supplementaryInformation": "ParticleEmitter3D::ParticleEmitter3D", + "type": "object" + } + ], + "objectGroups": [] + }, + { + "fullName": "", + "functionType": "ActionWithOperator", + "getterName": "TrailEndWidthRatio", + "group": "3D particle emitter trail configuration", + "name": "SetTrailEndWidthRatio", + "sentence": "", + "events": [ + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [], + "actions": [ + { + "type": { + "value": "SetNumberVariable" + }, + "parameters": [ + "TrailEndWidthRatio", + "=", + "Value" + ] + } + ] + }, + { + "type": "BuiltinCommonInstructions::JsCode", + "inlineCode": [ + "const object = objects[0];\r", + "const value = eventsFunctionContext.getArgument(\"Value\");\r", + "\r", + "object.__particleEmitterAdapter.setTrailEndWidthRatio(value);" + ], + "parameterObjects": "Object", + "useStrict": true, + "eventsSheetExpanded": false + } + ], + "parameters": [ + { + "description": "Object", + "name": "Object", + "supplementaryInformation": "ParticleEmitter3D::ParticleEmitter3D", + "type": "object" + } + ], + "objectGroups": [] + }, + { + "description": "the render mode of the object.", + "fullName": "Render mode", + "functionType": "ExpressionAndCondition", + "group": "3D particle emitter configuration", + "name": "RenderMode", + "private": true, + "sentence": "the render mode", + "events": [ + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [], + "actions": [ + { + "type": { + "value": "SetReturnString" + }, + "parameters": [ + "RenderMode" + ] + } + ] + } + ], + "expressionType": { + "supplementaryInformation": "[\"Billboard\",\"Trail\"]", + "type": "stringWithSelector" + }, + "parameters": [ + { + "description": "Object", + "name": "Object", + "supplementaryInformation": "ParticleEmitter3D::ParticleEmitter3D", + "type": "object" + } + ], + "objectGroups": [] + }, + { + "fullName": "", + "functionType": "ActionWithOperator", + "getterName": "RenderMode", + "group": "3D particle emitter configuration", + "name": "SetRenderMode", + "private": true, + "sentence": "", + "events": [ + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [], + "actions": [ + { + "type": { + "value": "SetStringVariable" + }, + "parameters": [ + "RenderMode", + "=", + "Value" + ] + } + ] + }, + { + "type": "BuiltinCommonInstructions::JsCode", + "inlineCode": [ + "const object = objects[0];\r", + "const value = eventsFunctionContext.getArgument(\"Value\");\r", + "\r", + "object.__particleEmitterAdapter.setRenderMode(value);" + ], + "parameterObjects": "Object", + "useStrict": true, + "eventsSheetExpanded": false + }, + { + "type": "BuiltinCommonInstructions::Comment", + "color": { + "b": 109, + "g": 230, + "r": 255, + "textB": 0, + "textG": 0, + "textR": 0 + }, + "comment": "Update the trail properties because the RenderMode setter reset them" + }, + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [ + { + "type": { + "value": "StringVariable" + }, + "parameters": [ + "RenderMode", + "=", + "\"Trail\"" + ] + } + ], + "actions": [ + { + "type": { + "value": "ParticleEmitter3D::ParticleEmitter3D::SetTrailStartLengthMin" + }, + "parameters": [ + "Object", + "=", + "TrailStartLengthMin", + "" + ] + }, + { + "type": { + "value": "ParticleEmitter3D::ParticleEmitter3D::SetTrailStartLengthMax" + }, + "parameters": [ + "Object", + "=", + "TrailStartLengthMax", + "" + ] + } + ] + } + ], + "parameters": [ + { + "description": "Object", + "name": "Object", + "supplementaryInformation": "ParticleEmitter3D::ParticleEmitter3D", + "type": "object" + } + ], + "objectGroups": [] + } + ], + "eventsFunctionsFolderStructure": { + "folderName": "__ROOT", + "children": [ + { + "functionName": "onCreated" + }, + { + "functionName": "onDestroy" + }, + { + "functionName": "onHotReloading" + }, + { + "functionName": "UpdateFromProperties" + }, + { + "functionName": "UpdateHelper" + }, + { + "functionName": "UpdateImage" + }, + { + "functionName": "doStepPostEvents" + }, + { + "functionName": "RegisterInLayer" + }, + { + "functionName": "Delete" + }, + { + "functionName": "HasEnded" + }, + { + "functionName": "Restart" + }, + { + "folderName": "Position", + "children": [ + { + "functionName": "Z" + }, + { + "functionName": "SetZ" + } + ] + }, + { + "folderName": "Angle", + "children": [ + { + "functionName": "RotationX" + }, + { + "functionName": "SetRotationX" + }, + { + "functionName": "RotationY" + }, + { + "functionName": "SetRotationY" + } + ] + }, + { + "folderName": "3D particle emitter color configuration", + "children": [ + { + "functionName": "StartColor" + }, + { + "functionName": "SetStartColor" + }, + { + "functionName": "EndColor" + }, + { + "functionName": "SetEndColor" + }, + { + "functionName": "StartOpacity" + }, + { + "functionName": "SetStartOpacity" + }, + { + "functionName": "EndOpacity" + }, + { + "functionName": "SetEndOpacity" + }, + { + "functionName": "Blending" + }, + { + "functionName": "SetBlending" + } + ] + }, + { + "folderName": "3D particle emitter configuration", + "children": [ + { + "functionName": "Flow" + }, + { + "functionName": "SetFlow" + }, + { + "functionName": "StartSizeMin" + }, + { + "functionName": "SetStartSizeMin" + }, + { + "functionName": "StartSizeMax" + }, + { + "functionName": "SetStartSizeMax" + }, + { + "functionName": "EndScale" + }, + { + "functionName": "SetEndScale" + }, + { + "functionName": "LifespanMin" + }, + { + "functionName": "SetLifespanMin" + }, + { + "functionName": "LifespanMax" + }, + { + "functionName": "SetLifespanMax" + }, + { + "functionName": "Duration" + }, + { + "functionName": "SetDuration" + }, + { + "functionName": "SpayConeAngle" + }, + { + "functionName": "SetSpayConeAngle" + }, + { + "functionName": "ShouldAutodestruct" + }, + { + "functionName": "SetShouldAutodestruct" + }, + { + "functionName": "RenderMode" + }, + { + "functionName": "SetRenderMode" + } + ] + }, + { + "folderName": "3D particle emitter speed configuration", + "children": [ + { + "functionName": "StartSpeedMin" + }, + { + "functionName": "SetStartSpeedMin" + }, + { + "functionName": "StartSpeedMax" + }, + { + "functionName": "SetStartSpeedMax" + }, + { + "functionName": "AreParticlesRelative" + }, + { + "functionName": "SetAreParticlesRelative" + }, + { + "functionName": "GravityTop" + }, + { + "functionName": "SetGravityTop" + }, + { + "functionName": "Gravity" + }, + { + "functionName": "SetGravity" + } + ] + }, + { + "folderName": "3D particle emitter trail configuration", + "children": [ + { + "functionName": "TrailStartLengthMin" + }, + { + "functionName": "SetTrailStartLengthMin" + }, + { + "functionName": "TrailStartLengthMax" + }, + { + "functionName": "SetTrailStartLengthMax" + }, + { + "functionName": "IsTrailFollowingLocalOrigin" + }, + { + "functionName": "SetIsTrailFollowingLocalOrigin" + }, + { + "functionName": "TrailEndWidthRatio" + }, + { + "functionName": "SetTrailEndWidthRatio" + } + ] + } + ] + }, + "propertyDescriptors": [ + { + "value": "255;0;0", + "type": "Color", + "label": "Start color", + "group": "Color", + "name": "StartColor" + }, + { + "value": "255;255;0", + "type": "Color", + "label": "End color", + "group": "Color", + "name": "EndColor" + }, + { + "value": "255", + "type": "Number", + "unit": "Dimensionless", + "label": "Start opacity", + "group": "Color", + "name": "StartOpacity" + }, + { + "value": "0", + "type": "Number", + "unit": "Dimensionless", + "label": "End opacity", + "group": "Color", + "name": "EndOpacity" + }, + { + "value": "50", + "type": "Number", + "label": "Flow of particles (particles per second)", + "name": "Flow" + }, + { + "value": "10", + "type": "Number", + "unit": "Pixel", + "label": "Start min size", + "group": "Size", + "name": "StartSizeMin" + }, + { + "value": "20", + "type": "Number", + "unit": "Pixel", + "label": "Start max size", + "group": "Size", + "name": "StartSizeMax" + }, + { + "value": "0", + "type": "Number", + "unit": "Dimensionless", + "label": "End scale", + "group": "Size", + "name": "EndScale" + }, + { + "value": "100", + "type": "Number", + "unit": "PixelSpeed", + "label": "Start min speed", + "group": "Speed", + "name": "StartSpeedMin" + }, + { + "value": "100", + "type": "Number", + "unit": "PixelSpeed", + "label": "Start max speed", + "group": "Speed", + "name": "StartSpeedMax" + }, + { + "value": "1", + "type": "Number", + "unit": "Second", + "label": "Min lifespan", + "name": "LifespanMin" + }, + { + "value": "2", + "type": "Number", + "unit": "Second", + "label": "Max lifespan", + "name": "LifespanMax" + }, + { + "value": "5", + "type": "Number", + "unit": "Second", + "label": "Emission duration", + "name": "Duration" + }, + { + "value": "", + "type": "Boolean", + "label": "Particles move with the emitter", + "group": "Speed", + "name": "AreParticlesRelative" + }, + { + "value": "30", + "type": "Number", + "unit": "DegreeAngle", + "label": "Spay cone angle", + "group": "Position", + "name": "SpayConeAngle" + }, + { + "value": "Additive", + "type": "Choice", + "label": "Blending", + "group": "Color", + "choices": [ + { + "label": "Normal", + "value": "Normal" + }, + { + "label": "Additive", + "value": "Additive" + }, + { + "label": "Subtractive", + "value": "Subtractive" + }, + { + "label": "Multiply", + "value": "Multiply" + }, + { + "label": "None", + "value": "None" + } + ], + "name": "Blending" + }, + { + "value": "Y-", + "type": "Choice", + "label": "Gravity top", + "group": "Speed", + "choices": [ + { + "label": "Y-", + "value": "Y-" + }, + { + "label": "Z+", + "value": "Z+" + } + ], + "name": "GravityTop" + }, + { + "value": "0", + "type": "Number", + "unit": "PixelAcceleration", + "label": "Gravity", + "group": "Speed", + "name": "Gravity" + }, + { + "value": "true", + "type": "Boolean", + "label": "Delete when emission ends", + "name": "ShouldAutodestruct" + }, + { + "value": "Center-center", + "type": "String", + "label": "", + "description": "Only used by the scene editor.", + "hidden": true, + "name": "ParentOrigin" + }, + { + "value": "0", + "type": "Number", + "unit": "Pixel", + "label": "Z (elevation)", + "description": "Deprecated", + "group": "Position", + "hidden": true, + "name": "Z" + }, + { + "value": "0", + "type": "Number", + "unit": "DegreeAngle", + "label": "Rotation on X axis", + "group": "Position", + "hidden": true, + "name": "RotationX" + }, + { + "value": "0", + "type": "Number", + "unit": "DegreeAngle", + "label": "Rotation on Y axis", + "group": "Position", + "hidden": true, + "name": "RotationY" + }, + { + "value": "120", + "type": "Number", + "unit": "Pixel", + "label": "Start min length", + "group": "Trail", + "advanced": true, + "name": "TrailStartLengthMin" + }, + { + "value": "120", + "type": "Number", + "unit": "Pixel", + "label": "Start max length", + "group": "Trail", + "advanced": true, + "name": "TrailStartLengthMax" + }, + { + "value": "Billboard", + "type": "Choice", + "label": "Render mode", + "choices": [ + { + "label": "Image facing the camera", + "value": "Billboard" + }, + { + "label": "Image following a trail", + "value": "Trail" + } + ], + "advanced": true, + "name": "RenderMode" + }, + { + "value": "", + "type": "Boolean", + "label": "Follow the object", + "group": "Trail", + "advanced": true, + "name": "IsTrailFollowingLocalOrigin" + }, + { + "value": "0", + "type": "Number", + "unit": "Dimensionless", + "label": "Tail end width ratio", + "group": "Trail", + "advanced": true, + "name": "TrailEndWidthRatio" + } + ], + "propertiesFolderStructure": { + "folderName": "__ROOT", + "children": [ + { + "propertyName": "Flow" + }, + { + "propertyName": "LifespanMin" + }, + { + "propertyName": "LifespanMax" + }, + { + "propertyName": "Duration" + }, + { + "propertyName": "ShouldAutodestruct" + }, + { + "propertyName": "RenderMode" + }, + { + "folderName": "Color", + "children": [ + { + "propertyName": "StartColor" + }, + { + "propertyName": "EndColor" + }, + { + "propertyName": "StartOpacity" + }, + { + "propertyName": "EndOpacity" + }, + { + "propertyName": "Blending" + } + ] + }, + { + "folderName": "Size", + "children": [ + { + "propertyName": "StartSizeMin" + }, + { + "propertyName": "StartSizeMax" + }, + { + "propertyName": "EndScale" + } + ] + }, + { + "folderName": "Trail", + "children": [ + { + "propertyName": "TrailStartLengthMin" + }, + { + "propertyName": "TrailStartLengthMax" + }, + { + "propertyName": "TrailEndWidthRatio" + }, + { + "propertyName": "IsTrailFollowingLocalOrigin" + } + ] + }, + { + "folderName": "Speed", + "children": [ + { + "propertyName": "StartSpeedMin" + }, + { + "propertyName": "StartSpeedMax" + }, + { + "propertyName": "AreParticlesRelative" + }, + { + "propertyName": "GravityTop" + }, + { + "propertyName": "Gravity" + } + ] + }, + { + "folderName": "Position", + "children": [ + { + "propertyName": "SpayConeAngle" + }, + { + "propertyName": "Z" + }, + { + "propertyName": "RotationX" + }, + { + "propertyName": "RotationY" + } + ] + }, + { + "propertyName": "ParentOrigin" + } + ] + }, + "variants": [] + } + ] } ], "externalLayouts": [] diff --git a/examples/starting-3d-vampire-survivor/assets/Collision.wav b/examples/starting-3d-vampire-survivor/assets/Collision.wav new file mode 100644 index 0000000000000000000000000000000000000000..5b89dda9865db53229ee501b111b0612758f968b GIT binary patch literal 18870 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 x+VlMVl>qr)eza@;{7jZ}^1s$%`qD$R`yp&Kp~ z^-1F#0b+&^?~!djl}Lah%Xi-SRd8Jo{?;3o-h)_@@SoEI=)F?aM7l^>yqk02c9S24QiaO4jwr%c}IEn=$@Iu|j z{A?dLBatx*q%rq|JS?4a|Lt#kE6DC0Rls21az!T6sTB=BB|cf0=hn7W5Tj2YYh_UTYzD|O1qCGKoBgnDQ^!7 zlxOgcU=(L3SI*wW5@3SY9PjRFnyg5Rw>yp@`_0$geX&9y2t^gk=3+ldi57KzAFG}Z zt#+FP$a-;l>dnP*u|mK!5AdGgZ(`akjHzkN;Zof2D7M=ex~}UY`H(K9HEdOE2BrL~ z=3J{pA`;v$; r5yj*z64LN`uAO!n*j9V(G7kR$?WdIAZVN{t00000NkvXXu0mjfzF)6> literal 0 HcmV?d00001 diff --git a/examples/starting-3d-vampire-survivor/assets/Flat dark joystick border.png b/examples/starting-3d-vampire-survivor/assets/Flat dark joystick border.png index 5ddd717e42b566f78f6441a2a92756a1e612dc74..b4e83e112eb9375cefbb86a6d044a972f8bf4dcf 100644 GIT binary patch literal 3751 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 delta 1615 zcmV-V2C(_39pDU*B!3BTNLh0L01FZT01FZU(%pXi000IONkl$95AT zsS59mS44;|gnx(N+J#8cGO*RzEFP~zospvV09F@@5#H?{@q=XL22*Ds#!`x*5VwTo z4s|@d9%0$U>nUU)7Q7)|FM+)wU6p^Om0hR{xm+$Q0v6(ikQ?aY<#~Q6%d$d$CM1W~ zyWPhizDPv)k<3iubUOVV^8QI7_f&ihfrsPx+j*f+tbbe*UkoCwg&3a*Lr*ruVzFoy zUqm9@#3sJr>w85}TrEIx+~)>gFGPHR2>TfQ+$co2A>jZL6U2uIsq=v;-uI97Tf#!c zjkg1?`t4wW(l~eGoe1k|FBqpvXt>pz0R}xt%4c&^K9fwX4@^h5 zhA#J6GvxxN40aI`y1hS ze!&%-b_4%zQ{cc6n4Tf#%mZu^VFU=K0%QaW3=gfpBS1iakgx!G5nx&i9P-2F^Eody za({+iKu&O?c?w*XhG)!&Cg zfawTOXzfh}xLhv3JP-m*OMpTeFcIK);zNMx39zpSvp$9g3nr zfC#WcT9783hTwA|M1W}tu#fn9Cn4X75Pt!tBf!4ms|47$7kny01ek^ZhY;Uj!w8>6 zhyddk;1J>)d>Td%qz4@0!NA)Q`a##Y5+IinffdyyMU3Qw=}11;@`9lUeB)Rq*dEK| zW_&OlZvDyC9AGz&&+VvSn1Fn6JsVgGaLWaiV4DGRMK73Uz@Lqm0wxIvi3pe`Ab)&O z4Vd(R`Km7PX8*^R)qv^#k9r_D-2d(P!fNoN{&CV;a6gbQJqatw?@HG=xAlWdCg|wAjV`RfH>hw#SQIrNJ#pg@LH)z6u$;Y3pt1%-8PLq%Cl9A&N?sF9;)t-u>03mkWhy=6?j?_NzDg8eR}?-TU?Se(VLoY37R;1oO<7?g%*V zdUWWH@ZG{NT+g?wF|Z1wmtB|_gj>-zEpuT^O@q_WODG2O&`T%==UFe4)8M4t)~7(t z;3}uwOl>lm+|wnY*K2C(`>`l#B^`1+nNP;R0`)#0M7U*vm~t^S`o$ERO@FN(-J(;1 z_z)o^D87>#A>lauQ*FPqkd4HIgaZ*my5d8Ghz}70;``jT3$b^+TCEOcSzd+yZiu~I zv#}}$7~0A6{AvLfMG>0U`|xJ4fLh_%q035G*N9Jlml|If9m<>K|aWyu_P!t1d&3|x5Z z^#%&djQe*yv*GR7eBdP;T6rzP(=RLGcKmF4OKAR@U`TkaUxkVd>k&09nX(WY;h_fm z(7aCR2!JNd+2eUyzL8gq@wyPgY+$C*BJCAJ7NfknL`Nwlg2YAF$w>KrNUF|acY0Pb z+4w{CTkRp=E%$PDBDE&8BFg(ZY;T0bA^K$5`q@vPQ+=-f4MOTa`w!ht=DUf6Hmd*t N002ovPDHLkV1i6p4Cnv= diff --git a/examples/starting-3d-vampire-survivor/assets/Flat dark joystick thumb.png b/examples/starting-3d-vampire-survivor/assets/Flat dark joystick thumb.png index 36f5262a9055b15bf36ba5ddee957e1fa854561e..8acf179f8660be36ccea1198105da35c07154fd7 100644 GIT binary patch delta 2913 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 LNkvXXu0mjflp>}T delta 1022 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 T=EY0=p4HsM$~fK7E#$udo_DYl diff --git a/examples/starting-3d-vampire-survivor/assets/starting_bulletfire.wav b/examples/starting-3d-vampire-survivor/assets/starting_bulletfire.wav new file mode 100644 index 0000000000000000000000000000000000000000..f7045dd18c0e6f5125fccbccfc36f5023656a915 GIT binary patch literal 6148 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 edhy=ZUaI$;!{ literal 0 HcmV?d00001 diff --git a/examples/starting-3d-vampire-survivor/starting-3d-vampire-survivor.json b/examples/starting-3d-vampire-survivor/starting-3d-vampire-survivor.json index 79dc6b190..8fab4b0f8 100644 --- a/examples/starting-3d-vampire-survivor/starting-3d-vampire-survivor.json +++ b/examples/starting-3d-vampire-survivor/starting-3d-vampire-survivor.json @@ -1,7 +1,7 @@ { "firstLayout": "", "gdVersion": { - "build": 272, + "build": 274, "major": 5, "minor": 6, "revision": 0 @@ -169,6 +169,34 @@ "metadata": "", "name": "assets\\starting_unit_red.glb", "userAdded": false + }, + { + "file": "assets/starting_bulletfire.wav", + "kind": "audio", + "metadata": "", + "name": "assets/starting_bulletfire.wav", + "preloadAsMusic": false, + "preloadAsSound": false, + "preloadInCache": false, + "userAdded": false + }, + { + "file": "assets/DirtParticle.png", + "kind": "image", + "metadata": "", + "name": "assets/DirtParticle.png", + "smoothed": true, + "userAdded": false + }, + { + "file": "assets/Collision.wav", + "kind": "audio", + "metadata": "", + "name": "assets/Collision.wav", + "preloadAsMusic": false, + "preloadAsSound": false, + "preloadInCache": false, + "userAdded": false } ] }, @@ -406,6 +434,7 @@ { "assetStoreId": "", "name": "RepeatingBackground", + "persistentUuid": "a378fe39-88da-4ce3-9c0d-0f03b0a55f0b", "type": "Scene3D::Cube3DObject", "variables": [], "effects": [], @@ -444,6 +473,7 @@ { "assetStoreId": "", "name": "Player", + "persistentUuid": "2db23608-901a-4049-96c9-1494a4608a4b", "type": "Scene3D::Model3DObject", "variables": [], "effects": [], @@ -479,6 +509,31 @@ "BulletLayer": "", "RandomizedAngle": 0 }, + { + "isFolded": true, + "name": "ShakeModel3D", + "type": "ShakeObject3D::ShakeModel3D", + "TranslationAmplitudeX": 0, + "TranslationAmplitudeY": 0, + "TranslationAmplitudeZ": 0, + "RotationAmplitudeX": 5, + "RotationAmplitudeY": 5, + "RotationAmplitudeZ": 5, + "Time": 0, + "Duration": 0, + "StartEasingDuration": 0, + "StopEasingDuration": 0, + "Frequency": 20, + "DeltaX": 0, + "DeltaY": 0, + "DeltaZ": 0, + "DeltaAngleX": 0, + "DeltaAngleY": 0, + "DeltaAngleZ": 0, + "NoiseTime": 0, + "Object3D": "Object3D", + "IsStartingAtCreation": false + }, { "name": "SmoothCamera", "type": "SmoothCamera::SmoothCamera", @@ -532,14 +587,13 @@ "IsCalledManually": false }, { - "isFolded": true, "name": "TopDownMovement", "type": "TopDownMovementBehavior::TopDownMovementBehavior", + "deceleration": 1200, "rotateObject": false, "acceleration": 700, "maxSpeed": 300, "angularMaxSpeed": 300, - "deceleration": 900, "allowDiagonals": true, "angleOffset": 0, "customIsometryAngle": 30, @@ -558,7 +612,7 @@ } ], "content": { - "centerLocation": "ModelOrigin", + "centerLocation": "ObjectCenter", "crossfadeDuration": 0.10000000149011612, "depth": 100, "height": 100, @@ -578,18 +632,45 @@ { "assetStoreId": "", "name": "Enemy", + "persistentUuid": "3764970f-535c-4c4a-941b-fe790c72ec47", "type": "Scene3D::Model3DObject", "variables": [ { "name": "Health", + "persistentUuid": "44e3e30c-28bc-4790-8265-e7a8c3c66433", "type": "number", "value": 3 } ], "effects": [], - "behaviors": [], + "behaviors": [ + { + "name": "ShakeModel3D", + "type": "ShakeObject3D::ShakeModel3D", + "TranslationAmplitudeX": 5, + "TranslationAmplitudeY": 5, + "TranslationAmplitudeZ": 0, + "RotationAmplitudeX": 5, + "RotationAmplitudeY": 5, + "RotationAmplitudeZ": 5, + "Time": 0, + "Duration": 0, + "StartEasingDuration": 0, + "StopEasingDuration": 0, + "Frequency": 7, + "DeltaX": 0, + "DeltaY": 0, + "DeltaZ": 0, + "DeltaAngleX": 0, + "DeltaAngleY": 0, + "DeltaAngleZ": 0, + "NoiseTime": 0, + "Object3D": "Object3D", + "IsStartingAtCreation": false + } + ], "content": { - "centerLocation": "ModelOrigin", + "centerLocation": "ObjectCenter", "crossfadeDuration": 0.10000000149011612, "depth": 100, "height": 100, @@ -609,6 +690,7 @@ { "assetStoreId": "", "name": "PlayerBullet", + "persistentUuid": "427e887c-e35a-41e0-bb32-2ae3c388200d", "type": "Scene3D::Model3DObject", "variables": [], "effects": [], @@ -622,11 +704,11 @@ "content": { "centerLocation": "ModelOrigin", "crossfadeDuration": 0.10000000149011612, - "depth": 5.14897379705328, - "height": 5.57319141561976, + "depth": 12, + "height": 12, "isCastingShadow": true, "isReceivingShadow": true, - "keepAspectRatio": true, + "keepAspectRatio": false, "materialType": "StandardWithoutMetalness", "modelResourceName": "assets\\missile_orange.glb", "originLocation": "ModelOrigin", @@ -636,6 +718,168 @@ "width": 33.45138142032624, "animations": [] } + }, + { + "assetStoreId": "", + "name": "DebrisParticle", + "persistentUuid": "ea59fbc2-0756-4499-9f60-ed6a6ab3194a", + "type": "ParticleEmitter3D::ParticleEmitter3D", + "variant": "", + "variables": [], + "effects": [], + "behaviors": [], + "content": { + "EndColor": "255;255;255", + "StartColor": "109;109;109", + "SpayConeAngle": 360, + "EndScale": 0.5, + "Gravity": 600, + "StartSpeedMax": 250, + "StartSizeMin": 16, + "Duration": 0.01, + "Flow": 2000, + "LifespanMin": 0.075, + "LifespanMax": 0.25, + "StartSizeMax": 16, + "StartSpeedMin": 150 + }, + "childrenContent": { + "Particle": { + "adaptCollisionMaskAutomatically": true, + "updateIfNotVisible": false, + "animations": [ + { + "name": "Image", + "useMultipleDirections": false, + "directions": [ + { + "looping": false, + "timeBetweenFrames": 0.08, + "sprites": [ + { + "hasCustomCollisionMask": true, + "image": "assets/DirtParticle.png", + "points": [], + "originPoint": { + "name": "origine", + "x": 0, + "y": 0 + }, + "centerPoint": { + "automatic": true, + "name": "centre", + "x": 0, + "y": 0 + }, + "customCollisionMask": [ + [ + { + "x": 0, + "y": 0 + }, + { + "x": 32, + "y": 0 + }, + { + "x": 32, + "y": 32 + }, + { + "x": 0, + "y": 32 + } + ] + ] + } + ] + } + ] + } + ] + } + } + }, + { + "assetStoreId": "", + "name": "LargeDebrisParticle", + "persistentUuid": "dc87cd57-af7d-434c-9513-46f3782261ad", + "type": "ParticleEmitter3D::ParticleEmitter3D", + "variant": "", + "variables": [], + "effects": [], + "behaviors": [], + "content": { + "EndColor": "255;255;255", + "StartColor": "109;109;109", + "SpayConeAngle": 360, + "EndScale": 1, + "Gravity": 600, + "StartSpeedMax": 500, + "StartSizeMin": 32, + "Duration": 0.01, + "Flow": 2000, + "LifespanMin": 0.075, + "LifespanMax": 0.25, + "StartSizeMax": 32, + "StartSpeedMin": 300 + }, + "childrenContent": { + "Particle": { + "adaptCollisionMaskAutomatically": true, + "updateIfNotVisible": false, + "animations": [ + { + "name": "Image", + "useMultipleDirections": false, + "directions": [ + { + "looping": false, + "timeBetweenFrames": 0.08, + "sprites": [ + { + "hasCustomCollisionMask": true, + "image": "assets/DirtParticle.png", + "points": [], + "originPoint": { + "name": "origine", + "x": 0, + "y": 0 + }, + "centerPoint": { + "automatic": true, + "name": "centre", + "x": 0, + "y": 0 + }, + "customCollisionMask": [ + [ + { + "x": 0, + "y": 0 + }, + { + "x": 32, + "y": 0 + }, + { + "x": 32, + "y": 32 + }, + { + "x": 0, + "y": 32 + } + ] + ] + } + ] + } + ] + } + ] + } + } } ], "objectsFolderStructure": { @@ -650,6 +894,12 @@ { "objectName": "Enemy" }, + { + "objectName": "DebrisParticle" + }, + { + "objectName": "LargeDebrisParticle" + }, { "objectName": "RepeatingBackground" }, @@ -864,6 +1114,37 @@ "Player.Object3D::Depth() / 2" ] } + ], + "events": [ + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [ + { + "type": { + "value": "FireBullet::FireBullet::HasJustFired" + }, + "parameters": [ + "Player", + "FireBullet", + "" + ] + } + ], + "actions": [ + { + "type": { + "value": "PlaySound" + }, + "parameters": [ + "", + "assets/starting_bulletfire.wav", + "", + "20", + "RandomWithStep(0.9,1.2,0.05)" + ] + } + ] + } ] }, { @@ -906,6 +1187,54 @@ "1" ] }, + { + "type": { + "value": "Create" + }, + "parameters": [ + "", + "DebrisParticle", + "PlayerBullet.CenterX()", + "PlayerBullet.CenterY()", + "" + ] + }, + { + "type": { + "value": "Scene3D::Base3DBehavior::SetCenterZ" + }, + "parameters": [ + "DebrisParticle", + "Object3D", + "=", + "PlayerBullet.Object3D::CenterZ()" + ] + }, + { + "type": { + "value": "ShakeObject3D::ShakeModel3D::Shake" + }, + "parameters": [ + "Enemy", + "ShakeModel3D", + "0.25", + "0", + "0.2", + "" + ] + }, + { + "type": { + "value": "PlaySound" + }, + "parameters": [ + "", + "assets/Collision.wav", + "", + "30", + "RandomWithStep(0.9,1.1,0.05)" + ] + }, { "type": { "value": "Delete" @@ -933,6 +1262,18 @@ } ], "actions": [ + { + "type": { + "value": "Create" + }, + "parameters": [ + "", + "LargeDebrisParticle", + "Enemy.CenterX()", + "Enemy.CenterY()", + "" + ] + }, { "type": { "value": "Delete" @@ -958,9 +1299,69 @@ "", "" ] + }, + { + "type": { + "value": "BuiltinCommonInstructions::Once" + }, + "parameters": [] } ], "actions": [ + { + "type": { + "value": "ShakeObject3D::ShakeModel3D::Shake" + }, + "parameters": [ + "Player", + "ShakeModel3D", + "2", + "0", + "0", + "" + ] + }, + { + "type": { + "value": "PlaySound" + }, + "parameters": [ + "", + "assets/Collision.wav", + "", + "90", + "RandomFloatInRange(0.7,0.9)" + ] + }, + { + "type": { + "value": "Create" + }, + "parameters": [ + "", + "LargeDebrisParticle", + "Player.CenterX()", + "Player.CenterY()", + "" + ] + }, + { + "type": { + "value": "ChangeTimeScale" + }, + "parameters": [ + "", + "0.15" + ] + }, + { + "type": { + "value": "Wait" + }, + "parameters": [ + "0.15" + ] + }, { "type": { "value": "Scene" @@ -1092,6 +1493,11 @@ "name": "Scale", "type": "ScalableCapability::ScalableBehavior" }, + { + "name": "ShakeModel3D", + "type": "ShakeObject3D::ShakeModel3D", + "EasingFactor": 0 + }, { "name": "SmoothCamera", "type": "SmoothCamera::SmoothCamera" @@ -20967,6 +21373,14107 @@ }, "eventsBasedBehaviors": [], "eventsBasedObjects": [] + }, + { + "author": "", + "category": "Visual effect", + "dimension": "3D", + "extensionNamespace": "", + "fullName": "3D particle emitter", + "gdevelopVersion": ">=5.5.222", + "helpPath": "", + "iconUrl": "data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iVVRGLTgiPz48IURPQ1RZUEUgc3ZnIFBVQkxJQyAiLS8vVzNDLy9EVEQgU1ZHIDEuMS8vRU4iICJodHRwOi8vd3d3LnczLm9yZy9HcmFwaGljcy9TVkcvMS4xL0RURC9zdmcxMS5kdGQiPjxzdmcgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIiB4bWxuczp4bGluaz0iaHR0cDovL3d3dy53My5vcmcvMTk5OS94bGluayIgdmVyc2lvbj0iMS4xIiBpZD0ibWRpLWZpcmUiIHdpZHRoPSIyNCIgaGVpZ2h0PSIyNCIgdmlld0JveD0iMCAwIDI0IDI0Ij48cGF0aCBkPSJNMTcuNjYgMTEuMkMxNy40MyAxMC45IDE3LjE1IDEwLjY0IDE2Ljg5IDEwLjM4QzE2LjIyIDkuNzggMTUuNDYgOS4zNSAxNC44MiA4LjcyQzEzLjMzIDcuMjYgMTMgNC44NSAxMy45NSAzQzEzIDMuMjMgMTIuMTcgMy43NSAxMS40NiA0LjMyQzguODcgNi40IDcuODUgMTAuMDcgOS4wNyAxMy4yMkM5LjExIDEzLjMyIDkuMTUgMTMuNDIgOS4xNSAxMy41NUM5LjE1IDEzLjc3IDkgMTMuOTcgOC44IDE0LjA1QzguNTcgMTQuMTUgOC4zMyAxNC4wOSA4LjE0IDEzLjkzQzguMDggMTMuODggOC4wNCAxMy44MyA4IDEzLjc2QzYuODcgMTIuMzMgNi42OSAxMC4yOCA3LjQ1IDguNjRDNS43OCAxMCA0Ljg3IDEyLjMgNSAxNC40N0M1LjA2IDE0Ljk3IDUuMTIgMTUuNDcgNS4yOSAxNS45N0M1LjQzIDE2LjU3IDUuNyAxNy4xNyA2IDE3LjdDNy4wOCAxOS40MyA4Ljk1IDIwLjY3IDEwLjk2IDIwLjkyQzEzLjEgMjEuMTkgMTUuMzkgMjAuOCAxNy4wMyAxOS4zMkMxOC44NiAxNy42NiAxOS41IDE1IDE4LjU2IDEyLjcyTDE4LjQzIDEyLjQ2QzE4LjIyIDEyIDE3LjY2IDExLjIgMTcuNjYgMTEuMk0xNC41IDE3LjVDMTQuMjIgMTcuNzQgMTMuNzYgMTggMTMuNCAxOC4xQzEyLjI4IDE4LjUgMTEuMTYgMTcuOTQgMTAuNSAxNy4yOEMxMS42OSAxNyAxMi40IDE2LjEyIDEyLjYxIDE1LjIzQzEyLjc4IDE0LjQzIDEyLjQ2IDEzLjc3IDEyLjMzIDEzQzEyLjIxIDEyLjI2IDEyLjIzIDExLjYzIDEyLjUgMTAuOTRDMTIuNjkgMTEuMzIgMTIuODkgMTEuNyAxMy4xMyAxMkMxMy45IDEzIDE1LjExIDEzLjQ0IDE1LjM3IDE0LjhDMTUuNDEgMTQuOTQgMTUuNDMgMTUuMDggMTUuNDMgMTUuMjNDMTUuNDYgMTYuMDUgMTUuMSAxNi45NSAxNC41IDE3LjVIMTQuNVoiIC8+PC9zdmc+", + "name": "ParticleEmitter3D", + "previewIconUrl": "https://asset-resources.gdevelop.io/public-resources/Icons/f2e5a34bf465f781866677762d385d6c8e9e8d203383f2df9a3b7e0fad6a2cb5_fire.svg", + "shortDescription": "Display a large number of particles in 3D to create visual effects in a 3D game.", + "version": "3.1.0", + "description": [ + "3D particle emitters let you create and display many small particles to simulate visual effects in your game — like fire, explosions, smoke, or dust.", + "", + "The parameters of the object can be configured in multiple different ways to create a specific desired effect. GDevelop will give a set of a pre-configured 3D particle emitters that you should try first.", + "", + "Use these 3D particle emitters in 3D games. For 2D games or particles that appear on a game's 2D user interface, use the 2D particle emitter object instead." + ], + "origin": { + "identifier": "ParticleEmitter3D", + "name": "gdevelop-extension-store" + }, + "tags": [ + "3d", + "particle", + "explosion", + "fire", + "smoke", + "splash", + "light" + ], + "authorIds": [ + "IWykYNRvhCZBN3vEgKEbBPOR3Oc2" + ], + "changelog": [ + { + "version": "3.0.0", + "breaking": "- the object rotates the other way around X axis." + }, + { + "version": "2.0.0", + "breaking": "- Object properties for position and rotation have been removed. They must be set with the instance editor or the action." + }, + { + "version": "1.0.0", + "breaking": "- Particles were 3 times too small." + } + ], + "dependencies": [], + "globalVariables": [], + "sceneVariables": [], + "eventsFunctions": [ + { + "description": "Define helper classes JavaScript code.", + "fullName": "Define helper classes", + "functionType": "Action", + "name": "DefineHelperClasses", + "private": true, + "sentence": "Define helper classes JavaScript code", + "events": [ + { + "type": "BuiltinCommonInstructions::JsCode", + "inlineCode": [ + "if (gdjs.__particleEmmiter3DExtension) {", + " return;", + "}", + "", + "const game = runtimeScene.getGame();", + "", + "class ParticleEmitter3DRenderer extends gdjs.CustomRuntimeObject3DRenderer {", + " constructor(", + " object,", + " instanceContainer,", + " parent", + " ) {", + " super(object, instanceContainer, parent);", + " }", + "", + " _updateThreeGroup() {", + " const threeObject3D = this.get3DRendererObject();", + "", + " threeObject3D.rotation.set(", + " gdjs.toRad(this._object.getRotationX()),", + " gdjs.toRad(this._object.getRotationY()),", + " gdjs.toRad(this._object.angle)", + " );", + "", + " threeObject3D.position.set(", + " this._object.getX(),", + " this._object.getY(),", + " this._object.getZ()", + " );", + "", + " // Force the scale to 1 because the particle emitter doesn't really has a size.", + " threeObject3D.scale.set(", + " this._object.isFlippedX() ? -1 : 1,", + " this._object.isFlippedY() ? -1 : 1,", + " this._object.isFlippedZ() ? -1 : 1", + " );", + "", + " threeObject3D.visible = !this._object.hidden;", + "", + " this._isContainerDirty = true;", + " }", + "}", + "", + "const coneLength = 64;", + "", + "class ParticleEmitterHelper extends THREE.Object3D {", + " /** @type {gdjs.CustomRuntimeObject3D} */", + " object;", + " /** @type {THREE.LineSegments} */", + " cone;", + " /** @type {THREE.LineSegments} */", + " centerLine;", + " /** @type {THREE.Mesh} */", + " originBox;", + "", + "\t/**", + " * @param gdjs.CustomRuntimeObject3D object", + "\t */", + " constructor(object) {", + " super();", + " this.object = object;", + " this.type = 'ParticleEmitterHelper';", + "", + " const coneGeometry = new THREE.BufferGeometry();", + " const conePositions = [", + " 0, 0, 0, 0, 0, 1,", + " 0, 0, 0, 1, 0, 1,", + " 0, 0, 0, - 1, 0, 1,", + " 0, 0, 0, 0, 1, 1,", + " 0, 0, 0, 0, - 1, 1", + " ];", + " for (let i = 0, j = 1, l = 32; i < l; i++ , j++) {", + " const p1 = (i / l) * Math.PI * 2;", + " const p2 = (j / l) * Math.PI * 2;", + " conePositions.push(", + " Math.cos(p1), Math.sin(p1), 1,", + " Math.cos(p2), Math.sin(p2), 1", + " );", + " }", + " coneGeometry.setAttribute('position', new THREE.Float32BufferAttribute(conePositions, 3));", + " this.cone = new THREE.LineSegments(", + " coneGeometry,", + " new THREE.LineBasicMaterial({ fog: false, toneMapped: false })", + " );", + " this.add(this.cone);", + " const centerLineGeometry = new THREE.BufferGeometry();", + " centerLineGeometry.setAttribute('position', new THREE.Float32BufferAttribute([", + " 0, 0, 0, 0, 0, coneLength", + " ], 3));", + " this.centerLine = new THREE.LineSegments(", + " centerLineGeometry,", + " new THREE.LineBasicMaterial({ fog: false, toneMapped: false })", + " );", + " this.add(this.centerLine);", + "", + " this.originBox = new THREE.Mesh(", + " new THREE.BoxGeometry(16, 16, 16),", + " new THREE.MeshBasicMaterial({ fog: false, toneMapped: false })", + " );", + " this.add(this.originBox);", + " this.update();", + "", + " this.originBox.gdjsRuntimeObject = object;", + " }", + "", + " dispose() {", + " this.cone.geometry.dispose();", + " this.cone.material.dispose();", + " this.centerLine.geometry.dispose();", + " this.centerLine.material.dispose();", + " this.originBox.geometry.dispose();", + " this.originBox.material.dispose();", + " }", + "", + " update() {", + " const coneWidth = coneLength * Math.sin(gdjs.toRad(this.object._getSpayConeAngle()));", + " const coneHeight = coneLength * Math.cos(gdjs.toRad(this.object._getSpayConeAngle()));", + " this.cone.scale.set(coneWidth, coneWidth, coneHeight);", + " const startColor = gdjs.rgbOrHexStringToNumber(this.object._getStartColor())", + " const endColor = gdjs.rgbOrHexStringToNumber(this.object._getEndColor());", + " this.cone.material.color.set(endColor);", + " this.centerLine.material.color.set(startColor);", + " this.originBox.material.color.set(startColor);", + " }", + "}", + "", + "/**", + " * @param {string} colorString", + " * @param {THREE.Vector4} threeColor", + " */", + "const setThreeColor = (colorString, threeColor = new THREE.Vector4()) => {", + " const integerColor = gdjs.rgbOrHexToRGBColor(colorString);", + " threeColor.x = integerColor[0] / 255;", + " threeColor.y = integerColor[1] / 255;", + " threeColor.z = integerColor[2] / 255;", + "};", + "", + "/**", + " * @param {string} integerOpacity", + " * @param {THREE.Vector4} threeColor", + " */", + "const setThreeOpacity = (integerOpacity, threeColor = new THREE.Vector4()) => {", + " threeColor.w = integerOpacity / 255;", + "};", + "", + "class ParticleEmitterAdapter {", + " /**", + " * @param particleSystem {ParticleSystem}", + " * @param colorOverLife {ColorOverLife}", + " * @param sizeOverLife {SizeOverLife}", + " * @param applyForce {ApplyForce}", + " * @param applyForce {ApplyForce}", + " * @param widthOverLength {WidthOverLength}", + " */", + " constructor(particleSystem, colorOverLife, sizeOverLife, applyForce, widthOverLength) {", + " this.particleSystem = particleSystem;", + " this.colorOverLife = colorOverLife;", + " this.sizeOverLife = sizeOverLife;", + " this.applyForce = applyForce;", + " this.widthOverLength = widthOverLength;", + " }", + "", + " /**", + " * @param renderMode {'Billboard' | 'Trail'}", + " */", + " setRenderMode(renderMode) {", + " switch (renderMode) {", + " case \"BillBoard\":", + " this.particleSystem.renderMode = RenderMode.BillBoard;", + " break;", + " case \"Trail\":", + " this.particleSystem.renderMode = RenderMode.Trail;", + " this.particleSystem.rendererEmitterSettings.startLength = new IntervalValue(30, 30);", + " break;", + " }", + " }", + "", + " /**", + " * @param startLengthMin {number}", + " */", + " setTrailStartLengthMin(startLengthMin) {", + " const startLength = this.particleSystem.rendererEmitterSettings.startLength;", + " if (!startLength) {", + " return;", + " }", + " startLength.a = startLengthMin;", + " }", + "", + " /**", + " * @param startLengthMax {number}", + " */", + " setTrailStartLengthMax(startLengthMax) {", + " const startLength = this.particleSystem.rendererEmitterSettings.startLength;", + " if (!startLength) {", + " return;", + " }", + " startLength.b = startLengthMax;", + " }", + "", + " /**", + " * @param followLocalOrigin {boolean}", + " */", + " setTrailFollowingLocalOrigin(followLocalOrigin) {", + " this.particleSystem.rendererEmitterSettings.followLocalOrigin = followLocalOrigin;", + " }", + "", + " /**", + " * @param trailEndWidthRatio {number}", + " */", + " setTrailEndWidthRatio(trailEndWidthRatio) {", + " // Check this comment in the lib code:", + " // We added a multiplication with size because it's more convenient for the extension.", + "", + " /** @type Bezier */", + " const bezier = this.widthOverLength.width.functions[0][0];", + " bezier.p[0] = 1;", + " bezier.p[1] = gdjs.evtTools.common.lerp(1, trailEndWidthRatio, 1 / 3);", + " bezier.p[2] = gdjs.evtTools.common.lerp(1, trailEndWidthRatio, 2 / 3);", + " bezier.p[3] = trailEndWidthRatio;", + " }", + "", + "", + " /**", + " * @param startColor {string}", + " */", + " setStartColor(startColor) {", + " setThreeColor(startColor, this.colorOverLife.color.color.keys[0][0]);", + " }", + "", + " /**", + " * @param endColor {string}", + " */", + " setEndColor(endColor) {", + " setThreeColor(endColor, this.colorOverLife.color.color.keys[1][0]);", + " }", + "", + " /**", + " * @param startOpacity {number}", + " */", + " setStartOpacity(startOpacity) {", + " this.colorOverLife.color.alpha.keys[0][0] = startOpacity / 255;", + " }", + "", + " /**", + " * @param endOpacity {number}", + " */", + " setEndOpacity(endOpacity) {", + " this.colorOverLife.color.alpha.keys[1][0] = endOpacity / 255;", + " }", + "", + " /**", + " * @param flow {number}", + " */", + " setFlow(flow) {", + " this.particleSystem.emissionOverTime.value = flow;", + " }", + "", + " /**", + " * @param startMinSize {number}", + " */", + " setStartMinSize(startMinSize) {", + " this.particleSystem.startSize.a = startMinSize;", + " }", + "", + " /**", + " * @param startMaxSize {number}", + " */", + " setStartMaxSize(startMaxSize) {", + " this.particleSystem.startSize.b = startMaxSize;", + " }", + "", + " /**", + " * @param endScale {number}", + " */", + " setEndScale(endScale) {", + " /** @type Bezier */", + " const bezier = this.sizeOverLife.size.functions[0][0];", + " bezier.p[0] = 1;", + " bezier.p[1] = gdjs.evtTools.common.lerp(1, endScale, 1 / 3);", + " bezier.p[2] = gdjs.evtTools.common.lerp(1, endScale, 2 / 3);", + " bezier.p[3] = endScale;", + " }", + "", + " /**", + " * @param startSpeedMin {number}", + " */", + " setStartSpeedMin(startSpeedMin) {", + " this.particleSystem.startSpeed.a = startSpeedMin;", + " }", + "", + " /**", + " * @param startSpeedMax {number}", + " */", + " setStartSpeedMax(startSpeedMax) {", + " this.particleSystem.startSpeed.b = startSpeedMax;", + " }", + "", + " /**", + " * @param lifespanMin {number}", + " */", + " setLifespanMin(lifespanMin) {", + " this.particleSystem.startLife.a = lifespanMin;", + " }", + "", + " /**", + " * @param lifespanMax {number}", + " */", + " setLifespanMax(lifespanMax) {", + " this.particleSystem.startLife.b = lifespanMax;", + " }", + "", + " /**", + " * @param duration {number}", + " */", + " setDuration(duration) {", + " this.particleSystem.duration = duration || Number.POSITIVE_INFINITY;", + " }", + "", + " /**", + " * @param areParticlesRelative {boolean}", + " */", + " setParticlesRelative(areParticlesRelative) {", + " this.particleSystem.worldSpace = !areParticlesRelative;", + " }", + "", + " /**", + " * @param sprayConeAngle {number}", + " */", + " setSprayConeAngle(sprayConeAngle) {", + " if (0 < sprayConeAngle && sprayConeAngle <= 180) {", + " if (!this.particleSystem.emitterShape.angle) {", + " this.particleSystem.emitterShape = new ConeEmitter({ radius: 0.1, angle: Math.PI / 8 });", + " }", + " this.particleSystem.emitterShape.angle = sprayConeAngle * Math.PI / 180;", + " }", + " else {", + " if (this.particleSystem.emitterShape.angle) {", + " this.particleSystem.emitterShape = new PointEmitter();", + " }", + " }", + " }", + "", + " /**", + " * @param blending {string}", + " */", + " setBlending(blendingString) {", + " const blending =", + " blendingString === \"Additive\" ? THREE.AdditiveBlending :", + " blendingString === \"Normal\" ? THREE.NormalBlending :", + " blendingString === \"Subtractive\" ? THREE.SubtractiveBlending :", + " blendingString === \"Multiply\" ? THREE.MultiplyBlending :", + " blendingString === \"None\" ? THREE.NoBlending :", + " THREE.AdditiveBlending;", + " // TODO This doesn't work.", + " this.particleSystem.blending = blending;", + " }", + "", + " /**", + " * @param gravity {number}", + " */", + " setGravity(gravity) {", + " this.applyForce.magnitude.value = gravity;", + " }", + "", + " /**", + " * @param gravityTop {string}", + " */", + " setGravityTop(gravityTop) {", + " // TODO Make gravity absolute even when \"relative\" is enabled. ", + " switch (gravityTop) {", + " case \"Z+\":", + " this.applyForce.direction.set(0, 0, -1);", + " break;", + "", + " case \"Y-\":", + " this.applyForce.direction.set(0, 1, 0);", + " break;", + " }", + " }", + "", + " setImage(resourceName) {", + " const texture = game.getImageManager().getThreeTexture(resourceName);", + " this.particleSystem.texture = texture;", + " }", + "}", + "", + "", + "", + "/**", + " * three.quarks v0.11.2 build Mon Jan 22 2024", + " * https://github.com/Alchemist0823/three.quarks#readme", + " * Copyright 2024 Alchemist0823 , MIT", + " */", + "", + "// The migration to addUpdateRange was backported without updating the library", + "// because GDevelop still uses Three.js 0.160.0.", + "", + "class ParticleEmitter extends THREE.Object3D {", + " constructor(system) {", + " super();", + " this.type = 'ParticleEmitter';", + " this.system = system;", + " }", + " clone() {", + " const system = this.system.clone();", + " system.emitter.copy(this, true);", + " return system.emitter;", + " }", + " dispose() { }", + " extractFromCache(cache) {", + " const values = [];", + " for (const key in cache) {", + " const data = cache[key];", + " delete data.metadata;", + " values.push(data);", + " }", + " return values;", + " }", + " toJSON(meta, options = {}) {", + " const isRootObject = meta === undefined || typeof meta === 'string';", + " const output = {};", + " if (isRootObject) {", + " meta = {", + " geometries: {},", + " materials: {},", + " textures: {},", + " images: {},", + " shapes: {},", + " skeletons: {},", + " animations: {},", + " nodes: {},", + " };", + " output.metadata = {", + " version: 4.5,", + " type: 'Object',", + " generator: 'Object3D.toJSON',", + " };", + " }", + " const object = {};", + " object.uuid = this.uuid;", + " object.type = this.type;", + " if (this.name !== '')", + " object.name = this.name;", + " if (this.castShadow === true)", + " object.castShadow = true;", + " if (this.receiveShadow === true)", + " object.receiveShadow = true;", + " if (this.visible === false)", + " object.visible = false;", + " if (this.frustumCulled === false)", + " object.frustumCulled = false;", + " if (this.renderOrder !== 0)", + " object.renderOrder = this.renderOrder;", + " if (JSON.stringify(this.userData) !== '{}')", + " object.userData = this.userData;", + " object.layers = this.layers.mask;", + " object.matrix = this.matrix.toArray();", + " if (this.matrixAutoUpdate === false)", + " object.matrixAutoUpdate = false;", + " if (this.system !== null)", + " object.ps = this.system.toJSON(meta, options);", + " if (this.children.length > 0) {", + " object.children = [];", + " for (let i = 0; i < this.children.length; i++) {", + " if (this.children[i].type !== 'ParticleSystemPreview') {", + " object.children.push(this.children[i].toJSON(meta).object);", + " }", + " }", + " }", + " if (isRootObject) {", + " const geometries = this.extractFromCache(meta.geometries);", + " const materials = this.extractFromCache(meta.materials);", + " const textures = this.extractFromCache(meta.textures);", + " const images = this.extractFromCache(meta.images);", + " if (geometries.length > 0)", + " output.geometries = geometries;", + " if (materials.length > 0)", + " output.materials = materials;", + " if (textures.length > 0)", + " output.textures = textures;", + " if (images.length > 0)", + " output.images = images;", + " }", + " output.object = object;", + " return output;", + " }", + "}", + "", + "class LinkedListNode {", + " constructor(data) {", + " this.data = data;", + " this.next = null;", + " this.prev = null;", + " }", + " hasPrev() {", + " return this.prev !== null;", + " }", + " hasNext() {", + " return this.next !== null;", + " }", + "}", + "class LinkedList {", + " constructor() {", + " this.length = 0;", + " this.head = this.tail = null;", + " }", + " isEmpty() {", + " return this.head === null;", + " }", + " clear() {", + " this.length = 0;", + " this.head = this.tail = null;", + " }", + " front() {", + " if (this.head === null)", + " return null;", + " return this.head.data;", + " }", + " back() {", + " if (this.tail === null)", + " return null;", + " return this.tail.data;", + " }", + " dequeue() {", + " if (this.head) {", + " const value = this.head.data;", + " this.head = this.head.next;", + " if (!this.head) {", + " this.tail = null;", + " }", + " else {", + " this.head.prev = null;", + " }", + " this.length--;", + " return value;", + " }", + " return undefined;", + " }", + " pop() {", + " if (this.tail) {", + " const value = this.tail.data;", + " this.tail = this.tail.prev;", + " if (!this.tail) {", + " this.head = null;", + " }", + " else {", + " this.tail.next = null;", + " }", + " this.length--;", + " return value;", + " }", + " return undefined;", + " }", + " queue(data) {", + " const node = new LinkedListNode(data);", + " if (!this.tail) {", + " this.tail = node;", + " }", + " if (this.head) {", + " this.head.prev = node;", + " node.next = this.head;", + " }", + " this.head = node;", + " this.length++;", + " }", + " push(data) {", + " const node = new LinkedListNode(data);", + " if (!this.head) {", + " this.head = node;", + " }", + " if (this.tail) {", + " this.tail.next = node;", + " node.prev = this.tail;", + " }", + " this.tail = node;", + " this.length++;", + " }", + " insertBefore(node, data) {", + " const newNode = new LinkedListNode(data);", + " newNode.next = node;", + " newNode.prev = node.prev;", + " if (newNode.prev !== null) {", + " newNode.prev.next = newNode;", + " }", + " newNode.next.prev = newNode;", + " if (node == this.head) {", + " this.head = newNode;", + " }", + " this.length++;", + " }", + " remove(data) {", + " if (this.head === null || this.tail === null) {", + " return;", + " }", + " let tempNode = this.head;", + " if (data === this.head.data) {", + " this.head = this.head.next;", + " }", + " if (data === this.tail.data) {", + " this.tail = this.tail.prev;", + " }", + " while (tempNode.next !== null && tempNode.data !== data) {", + " tempNode = tempNode.next;", + " }", + " if (tempNode.data === data) {", + " if (tempNode.prev !== null)", + " tempNode.prev.next = tempNode.next;", + " if (tempNode.next !== null)", + " tempNode.next.prev = tempNode.prev;", + " this.length--;", + " }", + " }", + " *values() {", + " let current = this.head;", + " while (current !== null) {", + " yield current.data;", + " current = current.next;", + " }", + " }", + "}", + "", + "class NodeParticle {", + " constructor() {", + " this.position = new THREE.Vector3();", + " this.velocity = new THREE.Vector3();", + " this.age = 0;", + " this.life = 1;", + " this.size = 1;", + " this.rotation = 0;", + " this.color = new THREE.Vector4(1, 1, 1, 1);", + " this.uvTile = 0;", + " }", + " get died() {", + " return this.age >= this.life;", + " }", + " reset() {", + " this.position.set(0, 0, 0);", + " this.velocity.set(0, 0, 0);", + " this.age = 0;", + " this.life = 1;", + " this.size = 1;", + " this.rotation = 0;", + " this.color.set(1, 1, 1, 1);", + " this.uvTile = 0;", + " }", + "}", + "class SpriteParticle {", + " constructor() {", + " this.startSpeed = 0;", + " this.startColor = new THREE.Vector4();", + " this.startSize = 1;", + " this.position = new THREE.Vector3();", + " this.velocity = new THREE.Vector3();", + " this.age = 0;", + " this.life = 1;", + " this.size = 1;", + " this.speedModifier = 1;", + " this.rotation = 0;", + " this.color = new THREE.Vector4();", + " this.uvTile = 0;", + " }", + " get died() {", + " return this.age >= this.life;", + " }", + "}", + "class RecordState {", + " constructor(position, size, color) {", + " this.position = position;", + " this.size = size;", + " this.color = color;", + " }", + "}", + "class TrailParticle {", + " constructor() {", + " this.startSpeed = 0;", + " this.startColor = new THREE.Vector4();", + " this.startSize = 1;", + " this.position = new THREE.Vector3();", + " this.velocity = new THREE.Vector3();", + " this.age = 0;", + " this.life = 1;", + " this.size = 1;", + " this.length = 100;", + " this.speedModifier = 1;", + " this.color = new THREE.Vector4();", + " this.previous = new LinkedList();", + " this.uvTile = 0;", + " }", + " update() {", + " if (this.age <= this.life) {", + " this.previous.push(new RecordState(this.position.clone(), this.size, this.color.clone()));", + " }", + " else {", + " if (this.previous.length > 0) {", + " this.previous.dequeue();", + " }", + " }", + " while (this.previous.length > this.length) {", + " this.previous.dequeue();", + " }", + " }", + " get died() {", + " return this.age >= this.life;", + " }", + " reset() {", + " this.previous.clear();", + " }", + "}", + "", + "class Bezier {", + " constructor(p1, p2, p3, p4) {", + " this.p = [p1, p2, p3, p4];", + " }", + " genValue(t) {", + " const t2 = t * t;", + " const t3 = t * t * t;", + " const mt = 1 - t;", + " const mt2 = mt * mt;", + " const mt3 = mt2 * mt;", + " return this.p[0] * mt3 + this.p[1] * mt2 * t * 3 + this.p[2] * mt * t2 * 3 + this.p[3] * t3;", + " }", + " derivativeCoefficients(points) {", + " const dpoints = [];", + " for (let p = points, c = p.length - 1; c > 0; c--) {", + " const list = [];", + " for (let j = 0; j < c; j++) {", + " const dpt = c * (p[j + 1] - p[j]);", + " list.push(dpt);", + " }", + " dpoints.push(list);", + " p = list;", + " }", + " return dpoints;", + " }", + " getSlope(t) {", + " const p = this.derivativeCoefficients(this.p)[0];", + " const mt = 1 - t;", + " const a = mt * mt;", + " const b = mt * t * 2;", + " const c = t * t;", + " return a * p[0] + b * p[1] + c * p[2];", + " }", + " controlCurve(d0, d1) {", + " this.p[1] = d0 / 3 + this.p[0];", + " this.p[2] = this.p[3] - d1 / 3;", + " }", + " hull(t) {", + " let p = this.p;", + " let _p = [], pt, idx = 0, i = 0, l = 0;", + " const q = [];", + " q[idx++] = p[0];", + " q[idx++] = p[1];", + " q[idx++] = p[2];", + " q[idx++] = p[3];", + " while (p.length > 1) {", + " _p = [];", + " for (i = 0, l = p.length - 1; i < l; i++) {", + " pt = t * p[i] + (1 - t) * p[i + 1];", + " q[idx++] = pt;", + " _p.push(pt);", + " }", + " p = _p;", + " }", + " return q;", + " }", + " split(t) {", + " const q = this.hull(t);", + " const result = {", + " left: new Bezier(q[0], q[4], q[7], q[9]),", + " right: new Bezier(q[9], q[8], q[6], q[3]),", + " span: q", + " };", + " return result;", + " }", + " clone() {", + " return new Bezier(this.p[0], this.p[1], this.p[2], this.p[3]);", + " }", + " toJSON() {", + " return {", + " p0: this.p[0],", + " p1: this.p[1],", + " p2: this.p[2],", + " p3: this.p[3],", + " };", + " }", + " static fromJSON(json) {", + " return new Bezier(json.p0, json.p1, json.p2, json.p3);", + " }", + "}", + "", + "const ColorToJSON = (color) => {", + " return { r: color.x, g: color.y, b: color.z, a: color.w };", + "};", + "const JSONToColor = (json) => {", + " return new THREE.Vector4(json.r, json.g, json.b, json.a);", + "};", + "const JSONToValue = (json, type) => {", + " switch (type) {", + " case 'Vector3':", + " return new THREE.Vector3(json.x, json.y, json.z);", + " case 'Vector4':", + " return new THREE.Vector4(json.x, json.y, json.z, json.w);", + " case 'Color':", + " return new THREE.Vector3(json.r, json.g, json.b);", + " case 'Number':", + " return json;", + " default:", + " return json;", + " }", + "};", + "const ValueToJSON = (value, type) => {", + " switch (type) {", + " case 'Vector3':", + " return { x: value.x, y: value.y, z: value.z };", + " case 'Vector4':", + " return { x: value.x, y: value.y, z: value.z, w: value.w };", + " case 'Color':", + " return { r: value.x, g: value.y, b: value.z };", + " case 'Number':", + " return value;", + " default:", + " return value;", + " }", + "};", + "", + "class RandomColor {", + " constructor(a, b) {", + " this.a = a;", + " this.b = b;", + " this.type = \"value\";", + " }", + " genColor(color) {", + " const rand = Math.random();", + " return color.copy(this.a).lerp(this.b, rand);", + " }", + " toJSON() {", + " return {", + " type: \"RandomColor\",", + " a: ColorToJSON(this.a),", + " b: ColorToJSON(this.b),", + " };", + " }", + " static fromJSON(json) {", + " return new RandomColor(JSONToColor(json.a), JSONToColor(json.b));", + " }", + " clone() {", + " return new RandomColor(this.a.clone(), this.b.clone());", + " }", + "}", + "", + "class ColorRange {", + " constructor(a, b) {", + " this.a = a;", + " this.b = b;", + " this.type = 'value';", + " }", + " genColor(color, t) {", + " return color.copy(this.a).lerp(this.b, Math.random());", + " }", + " toJSON() {", + " return {", + " type: 'ColorRange',", + " a: ColorToJSON(this.a),", + " b: ColorToJSON(this.b),", + " };", + " }", + " static fromJSON(json) {", + " return new ColorRange(JSONToColor(json.a), JSONToColor(json.b));", + " }", + " clone() {", + " return new ColorRange(this.a.clone(), this.b.clone());", + " }", + "}", + "", + "class ContinuousLinearFunction {", + " constructor(keys, subType) {", + " this.subType = subType;", + " this.type = 'function';", + " this.keys = keys;", + " }", + " findKey(t) {", + " let mid = 0;", + " let left = 0, right = this.keys.length - 1;", + " while (left + 1 < right) {", + " mid = Math.floor((left + right) / 2);", + " if (t < this.getStartX(mid))", + " right = mid - 1;", + " else if (t > this.getEndX(mid))", + " left = mid + 1;", + " else", + " return mid;", + " }", + " for (let i = left; i <= right; i++) {", + " if (t >= this.getStartX(i) && t <= this.getEndX(i))", + " return i;", + " }", + " return -1;", + " }", + " getStartX(index) {", + " return this.keys[index][1];", + " }", + " getEndX(index) {", + " if (index + 1 < this.keys.length)", + " return this.keys[index + 1][1];", + " return 1;", + " }", + " genValue(value, t) {", + " const index = this.findKey(t);", + " if (this.subType === 'Number') {", + " if (index === -1) {", + " return this.keys[0][0];", + " }", + " else if (index + 1 >= this.keys.length) {", + " return this.keys[this.keys.length - 1][0];", + " }", + " return ((this.keys[index + 1][0] - this.keys[index][0]) *", + " ((t - this.getStartX(index)) / (this.getEndX(index) - this.getStartX(index))) +", + " this.keys[index][0]);", + " }", + " else {", + " if (index === -1) {", + " return value.copy(this.keys[0][0]);", + " }", + " if (index + 1 >= this.keys.length) {", + " return value.copy(this.keys[this.keys.length - 1][0]);", + " }", + " return value", + " .copy(this.keys[index][0])", + " .lerp(this.keys[index + 1][0], (t - this.getStartX(index)) / (this.getEndX(index) - this.getStartX(index)));", + " }", + " }", + " toJSON() {", + " this.keys[0][0].constructor.name;", + " return {", + " type: 'CLinearFunction',", + " subType: this.subType,", + " keys: this.keys.map(([color, pos]) => ({ value: ValueToJSON(color, this.subType), pos: pos })),", + " };", + " }", + " static fromJSON(json) {", + " return new ContinuousLinearFunction(json.keys.map((pair) => [JSONToValue(pair.value, json.subType), pair.pos]), json.subType);", + " }", + " clone() {", + " if (this.subType === 'Number') {", + " return new ContinuousLinearFunction(this.keys.map(([value, pos]) => [value, pos]), this.subType);", + " }", + " else {", + " return new ContinuousLinearFunction(this.keys.map(([value, pos]) => [value.clone(), pos]), this.subType);", + " }", + " }", + "}", + "", + "const tempVec3 = new THREE.Vector3();", + "class Gradient {", + " constructor(color = [", + " [new THREE.Vector3(0, 0, 0), 0],", + " [new THREE.Vector3(1, 1, 1), 0],", + " ], alpha = [", + " [1, 0],", + " [1, 1],", + " ]) {", + " this.type = 'function';", + " this.color = new ContinuousLinearFunction(color, 'Color');", + " this.alpha = new ContinuousLinearFunction(alpha, 'Number');", + " }", + " genColor(color, t) {", + " this.color.genValue(tempVec3, t);", + " return color.set(tempVec3.x, tempVec3.y, tempVec3.z, this.alpha.genValue(1, t));", + " }", + " toJSON() {", + " return {", + " type: 'Gradient',", + " color: this.color.toJSON(),", + " alpha: this.alpha.toJSON(),", + " };", + " }", + " static fromJSON(json) {", + " if (json.functions) {", + " const keys = json.functions.map((func) => [ColorRange.fromJSON(func.function).a, func.start]);", + " if (json.functions.length > 0) {", + " keys.push([ColorRange.fromJSON(json.functions[json.functions.length - 1].function).b, 1]);", + " }", + " return new Gradient(keys.map((key) => [new THREE.Vector3(key[0].x, key[0].y, key[0].z), key[1]]), keys.map((key) => [key[0].w, key[1]]));", + " }", + " else {", + " const gradient = new Gradient();", + " gradient.alpha = ContinuousLinearFunction.fromJSON(json.alpha);", + " gradient.color = ContinuousLinearFunction.fromJSON(json.color);", + " return gradient;", + " }", + " }", + " clone() {", + " const gradient = new Gradient();", + " gradient.alpha = this.alpha.clone();", + " gradient.color = this.color.clone();", + " return gradient;", + " }", + "}", + "", + "const tempColor = new THREE.Vector4();", + "class RandomColorBetweenGradient {", + " constructor(gradient1, gradient2) {", + " this.type = 'memorizedFunction';", + " this.gradient1 = gradient1;", + " this.gradient2 = gradient2;", + " }", + " startGen(memory) {", + " memory.rand = Math.random();", + " }", + " genColor(color, t, memory) {", + " this.gradient1.genColor(color, t);", + " this.gradient2.genColor(tempColor, t);", + " if (memory && memory.rand) {", + " color.lerp(tempColor, memory.rand);", + " }", + " else {", + " color.lerp(tempColor, Math.random());", + " }", + " return color;", + " }", + " toJSON() {", + " return {", + " type: 'RandomColorBetweenGradient',", + " gradient1: this.gradient1.toJSON(),", + " gradient2: this.gradient2.toJSON(),", + " };", + " }", + " static fromJSON(json) {", + " return new RandomColorBetweenGradient(Gradient.fromJSON(json.gradient1), Gradient.fromJSON(json.gradient2));", + " }", + " clone() {", + " return new RandomColorBetweenGradient(this.gradient1.clone(), this.gradient2.clone());", + " }", + "}", + "", + "class ConstantColor {", + " constructor(color) {", + " this.color = color;", + " this.type = 'value';", + " }", + " genColor(color) {", + " return color.copy(this.color);", + " }", + " toJSON() {", + " return {", + " type: 'ConstantColor',", + " color: ColorToJSON(this.color),", + " };", + " }", + " static fromJSON(json) {", + " return new ConstantColor(JSONToColor(json.color));", + " }", + " clone() {", + " return new ConstantColor(this.color.clone());", + " }", + "}", + "function ColorGeneratorFromJSON(json) {", + " switch (json.type) {", + " case 'ConstantColor':", + " return ConstantColor.fromJSON(json);", + " case 'ColorRange':", + " return ColorRange.fromJSON(json);", + " case 'RandomColor':", + " return RandomColor.fromJSON(json);", + " case 'Gradient':", + " return Gradient.fromJSON(json);", + " case 'RandomColorBetweenGradient':", + " return RandomColorBetweenGradient.fromJSON(json);", + " default:", + " return new ConstantColor(new THREE.Vector4(1, 1, 1, 1));", + " }", + "}", + "", + "class ConstantValue {", + " constructor(value) {", + " this.value = value;", + " this.type = 'value';", + " }", + " genValue() {", + " return this.value;", + " }", + " toJSON() {", + " return {", + " type: \"ConstantValue\",", + " value: this.value", + " };", + " }", + " static fromJSON(json) {", + " return new ConstantValue(json.value);", + " }", + " clone() {", + " return new ConstantValue(this.value);", + " }", + "}", + "", + "class IntervalValue {", + " constructor(a, b) {", + " this.a = a;", + " this.b = b;", + " this.type = 'value';", + " }", + " genValue() {", + " return THREE.MathUtils.lerp(this.a, this.b, Math.random());", + " }", + " toJSON() {", + " return {", + " type: 'IntervalValue',", + " a: this.a,", + " b: this.b,", + " };", + " }", + " static fromJSON(json) {", + " return new IntervalValue(json.a, json.b);", + " }", + " clone() {", + " return new IntervalValue(this.a, this.b);", + " }", + "}", + "", + "class PiecewiseFunction {", + " constructor() {", + " this.functions = new Array();", + " }", + " findFunction(t) {", + " let mid = 0;", + " let left = 0, right = this.functions.length - 1;", + " while (left + 1 < right) {", + " mid = Math.floor((left + right) / 2);", + " if (t < this.getStartX(mid))", + " right = mid - 1;", + " else if (t > this.getEndX(mid))", + " left = mid + 1;", + " else", + " return mid;", + " }", + " for (let i = left; i <= right; i++) {", + " if (t >= this.functions[i][1] && t <= this.getEndX(i))", + " return i;", + " }", + " return -1;", + " }", + " getStartX(index) {", + " return this.functions[index][1];", + " }", + " setStartX(index, x) {", + " if (index > 0)", + " this.functions[index][1] = x;", + " }", + " getEndX(index) {", + " if (index + 1 < this.functions.length)", + " return this.functions[index + 1][1];", + " return 1;", + " }", + " setEndX(index, x) {", + " if (index + 1 < this.functions.length)", + " this.functions[index + 1][1] = x;", + " }", + " insertFunction(t, func) {", + " const index = this.findFunction(t);", + " this.functions.splice(index + 1, 0, [func, t]);", + " }", + " removeFunction(index) {", + " return this.functions.splice(index, 1)[0][0];", + " }", + " getFunction(index) {", + " return this.functions[index][0];", + " }", + " setFunction(index, func) {", + " this.functions[index][0] = func;", + " }", + " get numOfFunctions() {", + " return this.functions.length;", + " }", + "}", + "", + "class PiecewiseBezier extends PiecewiseFunction {", + " constructor(curves = [[new Bezier(0, 1.0 / 3, 1.0 / 3 * 2, 1), 0]]) {", + " super();", + " this.type = \"function\";", + " this.functions = curves;", + " }", + " genValue(t = 0) {", + " const index = this.findFunction(t);", + " if (index === -1) {", + " return 0;", + " }", + " return this.functions[index][0].genValue((t - this.getStartX(index)) / (this.getEndX(index) - this.getStartX(index)));", + " }", + " toSVG(length, segments) {", + " if (segments < 1)", + " return \"\";", + " let result = [\"M\", 0, this.functions[0][0].p[0]].join(\" \");", + " for (let i = 1.0 / segments; i <= 1; i += 1.0 / segments) {", + " result = [result, \"L\", i * length, this.genValue(i)].join(\" \");", + " }", + " return result;", + " }", + " toJSON() {", + " return {", + " type: \"PiecewiseBezier\",", + " functions: this.functions.map(([bezier, start]) => ({ function: bezier.toJSON(), start: start })),", + " };", + " }", + " static fromJSON(json) {", + " return new PiecewiseBezier(json.functions.map((piecewiseFunction) => ([Bezier.fromJSON(piecewiseFunction.function), piecewiseFunction.start])));", + " }", + " clone() {", + " return new PiecewiseBezier(this.functions.map(([bezier, start]) => ([bezier.clone(), start])));", + " }", + "}", + "", + "function ValueGeneratorFromJSON(json) {", + " switch (json.type) {", + " case 'ConstantValue':", + " return ConstantValue.fromJSON(json);", + " case 'IntervalValue':", + " return IntervalValue.fromJSON(json);", + " case 'PiecewiseBezier':", + " return PiecewiseBezier.fromJSON(json);", + " default:", + " return new ConstantValue(0);", + " }", + "}", + "", + "class RandomQuatGenerator {", + " constructor() {", + " this.type = \"rotation\";", + " }", + " genValue(quat, t) {", + " let x, y, z, u, v, w;", + " do {", + " x = Math.random() * 2 - 1;", + " y = Math.random() * 2 - 1;", + " z = x * x + y * y;", + " } while (z > 1);", + " do {", + " u = Math.random() * 2 - 1;", + " v = Math.random() * 2 - 1;", + " w = u * u + v * v;", + " } while (w > 1);", + " const s = Math.sqrt((1 - z) / w);", + " quat.set(x, y, s * u, s * v);", + " return quat;", + " }", + " toJSON() {", + " return {", + " type: \"RandomQuat\"", + " };", + " }", + " static fromJSON(json) {", + " return new RandomQuatGenerator();", + " }", + " clone() {", + " return new RandomQuatGenerator();", + " }", + "}", + "", + "class AxisAngleGenerator {", + " constructor(axis, angle) {", + " this.axis = axis;", + " this.angle = angle;", + " this.type = 'rotation';", + " }", + " genValue(quat, t) {", + " return quat.setFromAxisAngle(this.axis, this.angle.genValue(t));", + " }", + " toJSON() {", + " return {", + " type: 'AxisAngle',", + " axis: { x: this.axis.x, y: this.axis.y, z: this.axis.z },", + " angle: this.angle.toJSON(),", + " };", + " }", + " static fromJSON(json) {", + " return new AxisAngleGenerator(new THREE.Vector3(json.axis.x, json.axis.y, json.axis.z), ValueGeneratorFromJSON(json.angle));", + " }", + " clone() {", + " return new AxisAngleGenerator(this.axis.clone(), this.angle.clone());", + " }", + "}", + "", + "class EulerGenerator {", + " constructor(angleX, angleY, angleZ, eulerOrder) {", + " this.angleX = angleX;", + " this.angleY = angleY;", + " this.angleZ = angleZ;", + " this.type = 'rotation';", + " this.eular = new THREE.Euler(0, 0, 0, eulerOrder);", + " }", + " genValue(quat, t) {", + " this.eular.set(this.angleX.genValue(t), this.angleY.genValue(t), this.angleZ.genValue(t));", + " return quat.setFromEuler(this.eular);", + " }", + " toJSON() {", + " return {", + " type: 'Euler',", + " angleX: this.angleX.toJSON(),", + " angleY: this.angleY.toJSON(),", + " angleZ: this.angleZ.toJSON(),", + " eulerOrder: this.eular.order,", + " };", + " }", + " static fromJSON(json) {", + " return new EulerGenerator(ValueGeneratorFromJSON(json.angleX), ValueGeneratorFromJSON(json.angleY), ValueGeneratorFromJSON(json.angleZ), json.eulerOrder);", + " }", + " clone() {", + " return new EulerGenerator(this.angleX, this.angleY, this.angleZ, this.eular.order);", + " }", + "}", + "", + "function RotationGeneratorFromJSON(json) {", + " switch (json.type) {", + " case 'AxisAngle':", + " return AxisAngleGenerator.fromJSON(json);", + " case 'Euler':", + " return EulerGenerator.fromJSON(json);", + " case 'RandomQuat':", + " return RandomQuatGenerator.fromJSON(json);", + " default:", + " return new RandomQuatGenerator();", + " }", + "}", + "", + "function GeneratorFromJSON(json) {", + " switch (json.type) {", + " case 'ConstantValue':", + " case 'IntervalValue':", + " case 'PiecewiseBezier':", + " return ValueGeneratorFromJSON(json);", + " case 'AxisAngle':", + " case 'RandomQuat':", + " case 'Euler':", + " return RotationGeneratorFromJSON(json);", + " default:", + " return new ConstantValue(0);", + " }", + "}", + "", + "class ColorOverLife {", + " constructor(color) {", + " this.color = color;", + " this.type = 'ColorOverLife';", + " }", + " initialize(particle) {", + " if (this.color.type === 'memorizedFunction') {", + " particle.colorOverLifeMemory = {};", + " this.color.startGen(particle.colorOverLifeMemory);", + " }", + " }", + " update(particle, delta) {", + " if (this.color.type === 'memorizedFunction') {", + " this.color.genColor(particle.color, particle.age / particle.life, particle.colorOverLifeMemory);", + " }", + " else {", + " this.color.genColor(particle.color, particle.age / particle.life);", + " }", + " particle.color.x *= particle.startColor.x;", + " particle.color.y *= particle.startColor.y;", + " particle.color.z *= particle.startColor.z;", + " particle.color.w *= particle.startColor.w;", + " }", + " frameUpdate(delta) { }", + " toJSON() {", + " return {", + " type: this.type,", + " color: this.color.toJSON(),", + " };", + " }", + " static fromJSON(json) {", + " return new ColorOverLife(ColorGeneratorFromJSON(json.color));", + " }", + " clone() {", + " return new ColorOverLife(this.color.clone());", + " }", + " reset() { }", + "}", + "", + "class RotationOverLife {", + " constructor(angularVelocity) {", + " this.angularVelocity = angularVelocity;", + " this.type = 'RotationOverLife';", + " this.dynamic = !(angularVelocity instanceof ConstantValue || angularVelocity instanceof IntervalValue);", + " }", + " initialize(particle) {", + " this.dynamic = !(this.angularVelocity instanceof ConstantValue || this.angularVelocity instanceof IntervalValue);", + " if (!this.dynamic && typeof particle.rotation === 'number') {", + " particle.angularVelocity = this.angularVelocity.genValue();", + " }", + " }", + " update(particle, delta) {", + " if (!this.dynamic) {", + " if (typeof particle.rotation === 'number') {", + " particle.rotation += delta * particle.angularVelocity;", + " }", + " }", + " else {", + " if (typeof particle.rotation === 'number') {", + " particle.rotation +=", + " delta * this.angularVelocity.genValue(particle.age / particle.life);", + " }", + " }", + " }", + " toJSON() {", + " return {", + " type: this.type,", + " angularVelocity: this.angularVelocity.toJSON(),", + " };", + " }", + " static fromJSON(json) {", + " return new RotationOverLife(ValueGeneratorFromJSON(json.angularVelocity));", + " }", + " frameUpdate(delta) { }", + " clone() {", + " return new RotationOverLife(this.angularVelocity.clone());", + " }", + " reset() { }", + "}", + "", + "const IdentityQuaternion = new THREE.Quaternion();", + "class Rotation3DOverLife {", + " constructor(angularVelocity) {", + " this.angularVelocity = angularVelocity;", + " this.type = 'Rotation3DOverLife';", + " this.tempQuat = new THREE.Quaternion();", + " this.dynamic = !(angularVelocity instanceof RandomQuatGenerator);", + " }", + " initialize(particle) {", + " this.dynamic = !(this.angularVelocity instanceof RandomQuatGenerator);", + " if (particle.rotation instanceof THREE.Quaternion) {", + " particle.angularVelocity = new THREE.Quaternion();", + " this.angularVelocity.genValue(particle.angularVelocity);", + " }", + " }", + " update(particle, delta) {", + " if (particle.rotation instanceof THREE.Quaternion) {", + " if (!this.dynamic) {", + " this.tempQuat.slerpQuaternions(IdentityQuaternion, particle.angularVelocity, delta);", + " particle.rotation.multiply(this.tempQuat);", + " }", + " else {", + " this.angularVelocity.genValue(this.tempQuat, particle.age / particle.life);", + " this.tempQuat.slerpQuaternions(IdentityQuaternion, this.tempQuat, delta);", + " particle.rotation.multiply(this.tempQuat);", + " }", + " }", + " }", + " toJSON() {", + " return {", + " type: this.type,", + " angularVelocity: this.angularVelocity.toJSON(),", + " };", + " }", + " static fromJSON(json) {", + " return new Rotation3DOverLife(RotationGeneratorFromJSON(json.angularVelocity));", + " }", + " frameUpdate(delta) { }", + " clone() {", + " return new Rotation3DOverLife(this.angularVelocity.clone());", + " }", + " reset() { }", + "}", + "", + "class ForceOverLife {", + " initialize(particle) { }", + " constructor(x, y, z) {", + " this.x = x;", + " this.y = y;", + " this.z = z;", + " this.type = 'ForceOverLife';", + " this._temp = new THREE.Vector3();", + " }", + " update(particle, delta) {", + " this._temp.set(this.x.genValue(particle.age / particle.life), this.y.genValue(particle.age / particle.life), this.z.genValue(particle.age / particle.life));", + " particle.velocity.addScaledVector(this._temp, delta);", + " }", + " toJSON() {", + " return {", + " type: this.type,", + " x: this.x.toJSON(),", + " y: this.y.toJSON(),", + " z: this.z.toJSON(),", + " };", + " }", + " static fromJSON(json) {", + " return new ForceOverLife(ValueGeneratorFromJSON(json.x), ValueGeneratorFromJSON(json.y), ValueGeneratorFromJSON(json.z));", + " }", + " frameUpdate(delta) { }", + " clone() {", + " return new ForceOverLife(this.x.clone(), this.y.clone(), this.z.clone());", + " }", + " reset() { }", + "}", + "", + "class SizeOverLife {", + " initialize(particle) {", + " }", + " constructor(size) {", + " this.size = size;", + " this.type = 'SizeOverLife';", + " }", + " update(particle) {", + " particle.size = particle.startSize * this.size.genValue(particle.age / particle.life);", + " }", + " toJSON() {", + " return {", + " type: this.type,", + " size: this.size.toJSON(),", + " };", + " }", + " static fromJSON(json) {", + " return new SizeOverLife(ValueGeneratorFromJSON(json.size));", + " }", + " frameUpdate(delta) {", + " }", + " clone() {", + " return new SizeOverLife(this.size.clone());", + " }", + " reset() {", + " }", + "}", + "", + "class SpeedOverLife {", + " initialize(particle) {", + " }", + " constructor(speed) {", + " this.speed = speed;", + " this.type = 'SpeedOverLife';", + " }", + " update(particle) {", + " particle.speedModifier = this.speed.genValue(particle.age / particle.life);", + " }", + " toJSON() {", + " return {", + " type: this.type,", + " speed: this.speed.toJSON(),", + " };", + " }", + " static fromJSON(json) {", + " return new SpeedOverLife(ValueGeneratorFromJSON(json.speed));", + " }", + " frameUpdate(delta) {", + " }", + " clone() {", + " return new SpeedOverLife(this.speed.clone());", + " }", + " reset() {", + " }", + "}", + "", + "class FrameOverLife {", + " constructor(frame) {", + " this.frame = frame;", + " this.type = 'FrameOverLife';", + " }", + " initialize(particle) {", + " if (!(this.frame instanceof PiecewiseBezier)) {", + " particle.uvTile = Math.floor(this.frame.genValue(0) + 0.001);", + " }", + " }", + " update(particle, delta) {", + " if (this.frame instanceof PiecewiseBezier) {", + " particle.uvTile = Math.floor(this.frame.genValue(particle.age / particle.life) + 0.001);", + " }", + " }", + " frameUpdate(delta) { }", + " toJSON() {", + " return {", + " type: this.type,", + " frame: this.frame.toJSON(),", + " };", + " }", + " static fromJSON(json) {", + " return new FrameOverLife(ValueGeneratorFromJSON(json.frame));", + " }", + " clone() {", + " return new FrameOverLife(this.frame.clone());", + " }", + " reset() { }", + "}", + "", + "new THREE.Vector3(0, 0, 1);", + "class OrbitOverLife {", + " constructor(orbitSpeed, axis = new THREE.Vector3(0, 1, 0)) {", + " this.orbitSpeed = orbitSpeed;", + " this.axis = axis;", + " this.type = 'OrbitOverLife';", + " this.temp = new THREE.Vector3();", + " this.rotation = new THREE.Quaternion();", + " this.line = new THREE.Line3();", + " }", + " initialize(particle) {", + " }", + " update(particle, delta) {", + " this.line.set(new THREE.Vector3(0, 0, 0), this.axis);", + " this.line.closestPointToPoint(particle.position, false, this.temp);", + " this.rotation.setFromAxisAngle(this.axis, this.orbitSpeed.genValue(particle.age / particle.life) * delta);", + " particle.position.sub(this.temp);", + " particle.position.applyQuaternion(this.rotation);", + " particle.position.add(this.temp);", + " }", + " frameUpdate(delta) {", + " }", + " toJSON() {", + " return {", + " type: this.type,", + " orbitSpeed: this.orbitSpeed.toJSON(),", + " axis: [this.axis.x, this.axis.y, this.axis.z],", + " };", + " }", + " static fromJSON(json) {", + " return new OrbitOverLife(ValueGeneratorFromJSON(json.orbitSpeed), json.axis ? new THREE.Vector3(json.axis[0], json.axis[1], json.axis[2]) : undefined);", + " }", + " clone() {", + " return new OrbitOverLife(this.orbitSpeed.clone());", + " }", + " reset() {", + " }", + "}", + "", + "class WidthOverLength {", + " initialize(particle) {", + " }", + " constructor(width) {", + " this.width = width;", + " this.type = 'WidthOverLength';", + " }", + " update(particle) {", + " if (particle instanceof TrailParticle) {", + " const iter = particle.previous.values();", + " for (let i = 0; i < particle.previous.length; i++) {", + " const cur = iter.next();", + " // We added a multiplication with size because it's more convenient for the extension.", + " cur.value.size = this.width.genValue((particle.previous.length - i) / particle.length) * particle.size;", + " }", + " }", + " }", + " frameUpdate(delta) {", + " }", + " toJSON() {", + " return {", + " type: this.type,", + " width: this.width.toJSON(),", + " };", + " }", + " static fromJSON(json) {", + " return new WidthOverLength(ValueGeneratorFromJSON(json.width));", + " }", + " clone() {", + " return new WidthOverLength(this.width.clone());", + " }", + " reset() {", + " }", + "}", + "", + "class ApplyForce {", + " constructor(direction, magnitude) {", + " this.direction = direction;", + " this.magnitude = magnitude;", + " this.type = 'ApplyForce';", + " this.magnitudeValue = this.magnitude.genValue();", + " }", + " initialize(particle) {", + " }", + " update(particle, delta) {", + " particle.velocity.addScaledVector(this.direction, this.magnitudeValue * delta);", + " }", + " frameUpdate(delta) {", + " this.magnitudeValue = this.magnitude.genValue();", + " }", + " toJSON() {", + " return {", + " type: this.type,", + " direction: [this.direction.x, this.direction.y, this.direction.z],", + " magnitude: this.magnitude.toJSON(),", + " };", + " }", + " static fromJSON(json) {", + " var _a;", + " return new ApplyForce(new THREE.Vector3(json.direction[0], json.direction[1], json.direction[2]), ValueGeneratorFromJSON((_a = json.magnitude) !== null && _a !== void 0 ? _a : json.force));", + " }", + " clone() {", + " return new ApplyForce(this.direction.clone(), this.magnitude.clone());", + " }", + " reset() {", + " }", + "}", + "", + "class GravityForce {", + " constructor(center, magnitude) {", + " this.center = center;", + " this.magnitude = magnitude;", + " this.type = 'GravityForce';", + " this.temp = new THREE.Vector3();", + " }", + " initialize(particle) {", + " }", + " update(particle, delta) {", + " this.temp.copy(this.center).sub(particle.position).normalize();", + " particle.velocity.addScaledVector(this.temp, this.magnitude / particle.position.distanceToSquared(this.center) * delta);", + " }", + " frameUpdate(delta) {", + " }", + " toJSON() {", + " return {", + " type: this.type,", + " center: [this.center.x, this.center.y, this.center.z],", + " magnitude: this.magnitude,", + " };", + " }", + " static fromJSON(json) {", + " return new GravityForce(new THREE.Vector3(json.center[0], json.center[1], json.center[2]), json.magnitude);", + " }", + " clone() {", + " return new GravityForce(this.center.clone(), this.magnitude);", + " }", + " reset() {", + " }", + "}", + "", + "new THREE.Vector3(0, 0, 1);", + "class ChangeEmitDirection {", + " constructor(angle) {", + " this.angle = angle;", + " this.type = 'ChangeEmitDirection';", + " this._temp = new THREE.Vector3();", + " this._q = new THREE.Quaternion();", + " }", + " initialize(particle) {", + " const len = particle.velocity.length();", + " if (len == 0)", + " return;", + " particle.velocity.normalize();", + " if (particle.velocity.x === 0 && particle.velocity.y === 0) {", + " this._temp.set(0, particle.velocity.z, 0);", + " }", + " else {", + " this._temp.set(-particle.velocity.y, particle.velocity.x, 0);", + " }", + " this._q.setFromAxisAngle(this._temp.normalize(), this.angle.genValue());", + " this._temp.copy(particle.velocity);", + " particle.velocity.applyQuaternion(this._q);", + " this._q.setFromAxisAngle(this._temp, Math.random() * Math.PI * 2);", + " particle.velocity.applyQuaternion(this._q);", + " particle.velocity.setLength(len);", + " }", + " update(particle, delta) {", + " }", + " frameUpdate(delta) {", + " }", + " toJSON() {", + " return {", + " type: this.type,", + " angle: this.angle.toJSON(),", + " };", + " }", + " static fromJSON(json) {", + " return new ChangeEmitDirection(ValueGeneratorFromJSON(json.angle));", + " }", + " clone() {", + " return new ChangeEmitDirection(this.angle);", + " }", + " reset() {", + " }", + "}", + "", + "const VECTOR_ONE = new THREE.Vector3(1, 1, 1);", + "const VECTOR_Z = new THREE.Vector3(0, 0, 1);", + "let SubParticleEmitMode = void 0;", + "(function (SubParticleEmitMode) {", + " SubParticleEmitMode[SubParticleEmitMode[\"Death\"] = 0] = \"Death\";", + " SubParticleEmitMode[SubParticleEmitMode[\"Birth\"] = 1] = \"Birth\";", + " SubParticleEmitMode[SubParticleEmitMode[\"Frame\"] = 2] = \"Frame\";", + "})(SubParticleEmitMode || (SubParticleEmitMode = {}));", + "class EmitSubParticleSystem {", + " constructor(particleSystem, useVelocityAsBasis, subParticleSystem, mode = SubParticleEmitMode.Frame, emitProbability = 1) {", + " this.particleSystem = particleSystem;", + " this.useVelocityAsBasis = useVelocityAsBasis;", + " this.subParticleSystem = subParticleSystem;", + " this.mode = mode;", + " this.emitProbability = emitProbability;", + " this.type = 'EmitSubParticleSystem';", + " this.q_ = new THREE.Quaternion();", + " this.v_ = new THREE.Vector3();", + " this.v2_ = new THREE.Vector3();", + " this.subEmissions = new Array();", + " if (this.subParticleSystem && this.subParticleSystem.system) {", + " this.subParticleSystem.system.onlyUsedByOther = true;", + " }", + " }", + " initialize(particle) {", + " }", + " update(particle, delta) {", + " if (this.mode === SubParticleEmitMode.Frame) {", + " this.emit(particle, delta);", + " }", + " else if (this.mode === SubParticleEmitMode.Birth && particle.age === 0) {", + " this.emit(particle, delta);", + " }", + " else if (this.mode === SubParticleEmitMode.Death && particle.age + delta >= particle.life) {", + " this.emit(particle, delta);", + " }", + " }", + " emit(particle, delta) {", + " if (!this.subParticleSystem)", + " return;", + " if (Math.random() > this.emitProbability) {", + " return;", + " }", + " const m = new THREE.Matrix4();", + " this.setMatrixFromParticle(m, particle);", + " this.subEmissions.push({", + " burstIndex: 0,", + " burstWaveIndex: 0,", + " time: 0,", + " waitEmiting: 0,", + " matrix: m,", + " travelDistance: 0,", + " particle: particle,", + " });", + " }", + " frameUpdate(delta) {", + " if (!this.subParticleSystem)", + " return;", + " for (let i = 0; i < this.subEmissions.length; i++) {", + " if (this.subEmissions[i].time >= this.subParticleSystem.system.duration) {", + " this.subEmissions[i] = this.subEmissions[this.subEmissions.length - 1];", + " this.subEmissions.length = this.subEmissions.length - 1;", + " i--;", + " }", + " else {", + " let subEmissionState = this.subEmissions[i];", + " if (subEmissionState.particle && subEmissionState.particle.age < subEmissionState.particle.life) {", + " this.setMatrixFromParticle(subEmissionState.matrix, subEmissionState.particle);", + " }", + " else {", + " subEmissionState.particle = undefined;", + " }", + " this.subParticleSystem.system.emit(delta, subEmissionState, subEmissionState.matrix);", + " }", + " }", + " }", + " toJSON() {", + " return {", + " type: this.type,", + " subParticleSystem: this.subParticleSystem ? this.subParticleSystem.uuid : '',", + " useVelocityAsBasis: this.useVelocityAsBasis,", + " mode: this.mode,", + " emitProbability: this.emitProbability,", + " };", + " }", + " static fromJSON(json, particleSystem) {", + " return new EmitSubParticleSystem(particleSystem, json.useVelocityAsBasis, json.subParticleSystem, json.mode, json.emitProbability);", + " }", + " clone() {", + " return new EmitSubParticleSystem(this.particleSystem, this.useVelocityAsBasis, this.subParticleSystem, this.mode, this.emitProbability);", + " }", + " reset() { }", + " setMatrixFromParticle(m, particle) {", + " let rotation;", + " if (particle.rotation === undefined || this.useVelocityAsBasis) {", + " if (particle.velocity.x === 0 &&", + " particle.velocity.y === 0 &&", + " (particle.velocity.z === 1 || particle.velocity.z === 0)) {", + " m.set(1, 0, 0, particle.position.x, 0, 1, 0, particle.position.y, 0, 0, 1, particle.position.z, 0, 0, 0, 1);", + " }", + " else {", + " this.v_.copy(VECTOR_Z).cross(particle.velocity);", + " this.v2_.copy(particle.velocity).cross(this.v_);", + " const len = this.v_.length();", + " const len2 = this.v2_.length();", + " m.set(this.v_.x / len, this.v2_.x / len2, particle.velocity.x, particle.position.x, this.v_.y / len, this.v2_.y / len2, particle.velocity.y, particle.position.y, this.v_.z / len, this.v2_.z / len2, particle.velocity.z, particle.position.z, 0, 0, 0, 1);", + " }", + " }", + " else {", + " if (particle.rotation instanceof THREE.Quaternion) {", + " rotation = particle.rotation;", + " }", + " else {", + " this.q_.setFromAxisAngle(VECTOR_Z, particle.rotation);", + " rotation = this.q_;", + " }", + " m.compose(particle.position, rotation, VECTOR_ONE);", + " }", + " if (!this.particleSystem.worldSpace) {", + " m.multiplyMatrices(this.particleSystem.emitter.matrixWorld, m);", + " }", + " }", + "}", + "", + "const F2 = 0.5 * (Math.sqrt(3.0) - 1.0);", + "const G2 = (3.0 - Math.sqrt(3.0)) / 6.0;", + "const F3 = 1.0 / 3.0;", + "const G3 = 1.0 / 6.0;", + "const F4 = (Math.sqrt(5.0) - 1.0) / 4.0;", + "const G4 = (5.0 - Math.sqrt(5.0)) / 20.0;", + "const grad3 = new Float32Array([1, 1, 0,", + " -1, 1, 0,", + " 1, -1, 0,", + " -1, -1, 0,", + " 1, 0, 1,", + " -1, 0, 1,", + " 1, 0, -1,", + " -1, 0, -1,", + " 0, 1, 1,", + " 0, -1, 1,", + " 0, 1, -1,", + " 0, -1, -1]);", + "const grad4 = new Float32Array([0, 1, 1, 1, 0, 1, 1, -1, 0, 1, -1, 1, 0, 1, -1, -1,", + " 0, -1, 1, 1, 0, -1, 1, -1, 0, -1, -1, 1, 0, -1, -1, -1,", + " 1, 0, 1, 1, 1, 0, 1, -1, 1, 0, -1, 1, 1, 0, -1, -1,", + " -1, 0, 1, 1, -1, 0, 1, -1, -1, 0, -1, 1, -1, 0, -1, -1,", + " 1, 1, 0, 1, 1, 1, 0, -1, 1, -1, 0, 1, 1, -1, 0, -1,", + " -1, 1, 0, 1, -1, 1, 0, -1, -1, -1, 0, 1, -1, -1, 0, -1,", + " 1, 1, 1, 0, 1, 1, -1, 0, 1, -1, 1, 0, 1, -1, -1, 0,", + " -1, 1, 1, 0, -1, 1, -1, 0, -1, -1, 1, 0, -1, -1, -1, 0]);", + "class SimplexNoise {", + " constructor(randomOrSeed = Math.random) {", + " const random = typeof randomOrSeed == 'function' ? randomOrSeed : alea(randomOrSeed);", + " this.p = buildPermutationTable(random);", + " this.perm = new Uint8Array(512);", + " this.permMod12 = new Uint8Array(512);", + " for (let i = 0; i < 512; i++) {", + " this.perm[i] = this.p[i & 255];", + " this.permMod12[i] = this.perm[i] % 12;", + " }", + " }", + " noise2D(x, y) {", + " const permMod12 = this.permMod12;", + " const perm = this.perm;", + " let n0 = 0;", + " let n1 = 0;", + " let n2 = 0;", + " const s = (x + y) * F2;", + " const i = Math.floor(x + s);", + " const j = Math.floor(y + s);", + " const t = (i + j) * G2;", + " const X0 = i - t;", + " const Y0 = j - t;", + " const x0 = x - X0;", + " const y0 = y - Y0;", + " let i1, j1;", + " if (x0 > y0) {", + " i1 = 1;", + " j1 = 0;", + " }", + " else {", + " i1 = 0;", + " j1 = 1;", + " }", + " const x1 = x0 - i1 + G2;", + " const y1 = y0 - j1 + G2;", + " const x2 = x0 - 1.0 + 2.0 * G2;", + " const y2 = y0 - 1.0 + 2.0 * G2;", + " const ii = i & 255;", + " const jj = j & 255;", + " let t0 = 0.5 - x0 * x0 - y0 * y0;", + " if (t0 >= 0) {", + " const gi0 = permMod12[ii + perm[jj]] * 3;", + " t0 *= t0;", + " n0 = t0 * t0 * (grad3[gi0] * x0 + grad3[gi0 + 1] * y0);", + " }", + " let t1 = 0.5 - x1 * x1 - y1 * y1;", + " if (t1 >= 0) {", + " const gi1 = permMod12[ii + i1 + perm[jj + j1]] * 3;", + " t1 *= t1;", + " n1 = t1 * t1 * (grad3[gi1] * x1 + grad3[gi1 + 1] * y1);", + " }", + " let t2 = 0.5 - x2 * x2 - y2 * y2;", + " if (t2 >= 0) {", + " const gi2 = permMod12[ii + 1 + perm[jj + 1]] * 3;", + " t2 *= t2;", + " n2 = t2 * t2 * (grad3[gi2] * x2 + grad3[gi2 + 1] * y2);", + " }", + " return 70.0 * (n0 + n1 + n2);", + " }", + " noise3D(x, y, z) {", + " const permMod12 = this.permMod12;", + " const perm = this.perm;", + " let n0, n1, n2, n3;", + " const s = (x + y + z) * F3;", + " const i = Math.floor(x + s);", + " const j = Math.floor(y + s);", + " const k = Math.floor(z + s);", + " const t = (i + j + k) * G3;", + " const X0 = i - t;", + " const Y0 = j - t;", + " const Z0 = k - t;", + " const x0 = x - X0;", + " const y0 = y - Y0;", + " const z0 = z - Z0;", + " let i1, j1, k1;", + " let i2, j2, k2;", + " if (x0 >= y0) {", + " if (y0 >= z0) {", + " i1 = 1;", + " j1 = 0;", + " k1 = 0;", + " i2 = 1;", + " j2 = 1;", + " k2 = 0;", + " }", + " else if (x0 >= z0) {", + " i1 = 1;", + " j1 = 0;", + " k1 = 0;", + " i2 = 1;", + " j2 = 0;", + " k2 = 1;", + " }", + " else {", + " i1 = 0;", + " j1 = 0;", + " k1 = 1;", + " i2 = 1;", + " j2 = 0;", + " k2 = 1;", + " }", + " }", + " else {", + " if (y0 < z0) {", + " i1 = 0;", + " j1 = 0;", + " k1 = 1;", + " i2 = 0;", + " j2 = 1;", + " k2 = 1;", + " }", + " else if (x0 < z0) {", + " i1 = 0;", + " j1 = 1;", + " k1 = 0;", + " i2 = 0;", + " j2 = 1;", + " k2 = 1;", + " }", + " else {", + " i1 = 0;", + " j1 = 1;", + " k1 = 0;", + " i2 = 1;", + " j2 = 1;", + " k2 = 0;", + " }", + " }", + " const x1 = x0 - i1 + G3;", + " const y1 = y0 - j1 + G3;", + " const z1 = z0 - k1 + G3;", + " const x2 = x0 - i2 + 2.0 * G3;", + " const y2 = y0 - j2 + 2.0 * G3;", + " const z2 = z0 - k2 + 2.0 * G3;", + " const x3 = x0 - 1.0 + 3.0 * G3;", + " const y3 = y0 - 1.0 + 3.0 * G3;", + " const z3 = z0 - 1.0 + 3.0 * G3;", + " const ii = i & 255;", + " const jj = j & 255;", + " const kk = k & 255;", + " let t0 = 0.6 - x0 * x0 - y0 * y0 - z0 * z0;", + " if (t0 < 0)", + " n0 = 0.0;", + " else {", + " const gi0 = permMod12[ii + perm[jj + perm[kk]]] * 3;", + " t0 *= t0;", + " n0 = t0 * t0 * (grad3[gi0] * x0 + grad3[gi0 + 1] * y0 + grad3[gi0 + 2] * z0);", + " }", + " let t1 = 0.6 - x1 * x1 - y1 * y1 - z1 * z1;", + " if (t1 < 0)", + " n1 = 0.0;", + " else {", + " const gi1 = permMod12[ii + i1 + perm[jj + j1 + perm[kk + k1]]] * 3;", + " t1 *= t1;", + " n1 = t1 * t1 * (grad3[gi1] * x1 + grad3[gi1 + 1] * y1 + grad3[gi1 + 2] * z1);", + " }", + " let t2 = 0.6 - x2 * x2 - y2 * y2 - z2 * z2;", + " if (t2 < 0)", + " n2 = 0.0;", + " else {", + " const gi2 = permMod12[ii + i2 + perm[jj + j2 + perm[kk + k2]]] * 3;", + " t2 *= t2;", + " n2 = t2 * t2 * (grad3[gi2] * x2 + grad3[gi2 + 1] * y2 + grad3[gi2 + 2] * z2);", + " }", + " let t3 = 0.6 - x3 * x3 - y3 * y3 - z3 * z3;", + " if (t3 < 0)", + " n3 = 0.0;", + " else {", + " const gi3 = permMod12[ii + 1 + perm[jj + 1 + perm[kk + 1]]] * 3;", + " t3 *= t3;", + " n3 = t3 * t3 * (grad3[gi3] * x3 + grad3[gi3 + 1] * y3 + grad3[gi3 + 2] * z3);", + " }", + " return 32.0 * (n0 + n1 + n2 + n3);", + " }", + " noise4D(x, y, z, w) {", + " const perm = this.perm;", + " let n0, n1, n2, n3, n4;", + " const s = (x + y + z + w) * F4;", + " const i = Math.floor(x + s);", + " const j = Math.floor(y + s);", + " const k = Math.floor(z + s);", + " const l = Math.floor(w + s);", + " const t = (i + j + k + l) * G4;", + " const X0 = i - t;", + " const Y0 = j - t;", + " const Z0 = k - t;", + " const W0 = l - t;", + " const x0 = x - X0;", + " const y0 = y - Y0;", + " const z0 = z - Z0;", + " const w0 = w - W0;", + " let rankx = 0;", + " let ranky = 0;", + " let rankz = 0;", + " let rankw = 0;", + " if (x0 > y0)", + " rankx++;", + " else", + " ranky++;", + " if (x0 > z0)", + " rankx++;", + " else", + " rankz++;", + " if (x0 > w0)", + " rankx++;", + " else", + " rankw++;", + " if (y0 > z0)", + " ranky++;", + " else", + " rankz++;", + " if (y0 > w0)", + " ranky++;", + " else", + " rankw++;", + " if (z0 > w0)", + " rankz++;", + " else", + " rankw++;", + " const i1 = rankx >= 3 ? 1 : 0;", + " const j1 = ranky >= 3 ? 1 : 0;", + " const k1 = rankz >= 3 ? 1 : 0;", + " const l1 = rankw >= 3 ? 1 : 0;", + " const i2 = rankx >= 2 ? 1 : 0;", + " const j2 = ranky >= 2 ? 1 : 0;", + " const k2 = rankz >= 2 ? 1 : 0;", + " const l2 = rankw >= 2 ? 1 : 0;", + " const i3 = rankx >= 1 ? 1 : 0;", + " const j3 = ranky >= 1 ? 1 : 0;", + " const k3 = rankz >= 1 ? 1 : 0;", + " const l3 = rankw >= 1 ? 1 : 0;", + " const x1 = x0 - i1 + G4;", + " const y1 = y0 - j1 + G4;", + " const z1 = z0 - k1 + G4;", + " const w1 = w0 - l1 + G4;", + " const x2 = x0 - i2 + 2.0 * G4;", + " const y2 = y0 - j2 + 2.0 * G4;", + " const z2 = z0 - k2 + 2.0 * G4;", + " const w2 = w0 - l2 + 2.0 * G4;", + " const x3 = x0 - i3 + 3.0 * G4;", + " const y3 = y0 - j3 + 3.0 * G4;", + " const z3 = z0 - k3 + 3.0 * G4;", + " const w3 = w0 - l3 + 3.0 * G4;", + " const x4 = x0 - 1.0 + 4.0 * G4;", + " const y4 = y0 - 1.0 + 4.0 * G4;", + " const z4 = z0 - 1.0 + 4.0 * G4;", + " const w4 = w0 - 1.0 + 4.0 * G4;", + " const ii = i & 255;", + " const jj = j & 255;", + " const kk = k & 255;", + " const ll = l & 255;", + " let t0 = 0.6 - x0 * x0 - y0 * y0 - z0 * z0 - w0 * w0;", + " if (t0 < 0)", + " n0 = 0.0;", + " else {", + " const gi0 = (perm[ii + perm[jj + perm[kk + perm[ll]]]] % 32) * 4;", + " t0 *= t0;", + " n0 = t0 * t0 * (grad4[gi0] * x0 + grad4[gi0 + 1] * y0 + grad4[gi0 + 2] * z0 + grad4[gi0 + 3] * w0);", + " }", + " let t1 = 0.6 - x1 * x1 - y1 * y1 - z1 * z1 - w1 * w1;", + " if (t1 < 0)", + " n1 = 0.0;", + " else {", + " const gi1 = (perm[ii + i1 + perm[jj + j1 + perm[kk + k1 + perm[ll + l1]]]] % 32) * 4;", + " t1 *= t1;", + " n1 = t1 * t1 * (grad4[gi1] * x1 + grad4[gi1 + 1] * y1 + grad4[gi1 + 2] * z1 + grad4[gi1 + 3] * w1);", + " }", + " let t2 = 0.6 - x2 * x2 - y2 * y2 - z2 * z2 - w2 * w2;", + " if (t2 < 0)", + " n2 = 0.0;", + " else {", + " const gi2 = (perm[ii + i2 + perm[jj + j2 + perm[kk + k2 + perm[ll + l2]]]] % 32) * 4;", + " t2 *= t2;", + " n2 = t2 * t2 * (grad4[gi2] * x2 + grad4[gi2 + 1] * y2 + grad4[gi2 + 2] * z2 + grad4[gi2 + 3] * w2);", + " }", + " let t3 = 0.6 - x3 * x3 - y3 * y3 - z3 * z3 - w3 * w3;", + " if (t3 < 0)", + " n3 = 0.0;", + " else {", + " const gi3 = (perm[ii + i3 + perm[jj + j3 + perm[kk + k3 + perm[ll + l3]]]] % 32) * 4;", + " t3 *= t3;", + " n3 = t3 * t3 * (grad4[gi3] * x3 + grad4[gi3 + 1] * y3 + grad4[gi3 + 2] * z3 + grad4[gi3 + 3] * w3);", + " }", + " let t4 = 0.6 - x4 * x4 - y4 * y4 - z4 * z4 - w4 * w4;", + " if (t4 < 0)", + " n4 = 0.0;", + " else {", + " const gi4 = (perm[ii + 1 + perm[jj + 1 + perm[kk + 1 + perm[ll + 1]]]] % 32) * 4;", + " t4 *= t4;", + " n4 = t4 * t4 * (grad4[gi4] * x4 + grad4[gi4 + 1] * y4 + grad4[gi4 + 2] * z4 + grad4[gi4 + 3] * w4);", + " }", + " return 27.0 * (n0 + n1 + n2 + n3 + n4);", + " }", + "}", + "function buildPermutationTable(random) {", + " const p = new Uint8Array(256);", + " for (let i = 0; i < 256; i++) {", + " p[i] = i;", + " }", + " for (let i = 0; i < 255; i++) {", + " const r = i + ~~(random() * (256 - i));", + " const aux = p[i];", + " p[i] = p[r];", + " p[r] = aux;", + " }", + " return p;", + "}", + "function alea(seed) {", + " let s0 = 0;", + " let s1 = 0;", + " let s2 = 0;", + " let c = 1;", + " const mash = masher();", + " s0 = mash(' ');", + " s1 = mash(' ');", + " s2 = mash(' ');", + " s0 -= mash(seed);", + " if (s0 < 0) {", + " s0 += 1;", + " }", + " s1 -= mash(seed);", + " if (s1 < 0) {", + " s1 += 1;", + " }", + " s2 -= mash(seed);", + " if (s2 < 0) {", + " s2 += 1;", + " }", + " return function () {", + " const t = 2091639 * s0 + c * 2.3283064365386963e-10;", + " s0 = s1;", + " s1 = s2;", + " return s2 = t - (c = t | 0);", + " };", + "}", + "function masher() {", + " let n = 0xefc8249d;", + " return function (data) {", + " data = data.toString();", + " for (let i = 0; i < data.length; i++) {", + " n += data.charCodeAt(i);", + " let h = 0.02519603282416938 * n;", + " n = h >>> 0;", + " h -= n;", + " h *= n;", + " n = h >>> 0;", + " h -= n;", + " n += h * 0x100000000;", + " }", + " return (n >>> 0) * 2.3283064365386963e-10;", + " };", + "}", + "", + "class TurbulenceField {", + " constructor(scale, octaves, velocityMultiplier, timeScale) {", + " this.scale = scale;", + " this.octaves = octaves;", + " this.velocityMultiplier = velocityMultiplier;", + " this.timeScale = timeScale;", + " this.type = 'TurbulenceField';", + " this.generator = new SimplexNoise();", + " this.timeOffset = new THREE.Vector3();", + " this.temp = new THREE.Vector3();", + " this.temp2 = new THREE.Vector3();", + " this.timeOffset.x = Math.random() / this.scale.x * this.timeScale.x;", + " this.timeOffset.y = Math.random() / this.scale.y * this.timeScale.y;", + " this.timeOffset.z = Math.random() / this.scale.z * this.timeScale.z;", + " }", + " initialize(particle) {", + " }", + " update(particle, delta) {", + " const x = particle.position.x / this.scale.x;", + " const y = particle.position.y / this.scale.y;", + " const z = particle.position.z / this.scale.z;", + " this.temp.set(0, 0, 0);", + " let lvl = 1;", + " for (let i = 0; i < this.octaves; i++) {", + " this.temp2.set(this.generator.noise4D(x * lvl, y * lvl, z * lvl, this.timeOffset.x * lvl) / lvl, this.generator.noise4D(x * lvl, y * lvl, z * lvl, this.timeOffset.y * lvl) / lvl, this.generator.noise4D(x * lvl, y * lvl, z * lvl, this.timeOffset.z * lvl) / lvl);", + " this.temp.add(this.temp2);", + " lvl *= 2;", + " }", + " this.temp.multiply(this.velocityMultiplier);", + " particle.velocity.addScaledVector(this.temp, delta);", + " }", + " toJSON() {", + " return {", + " type: this.type,", + " scale: [this.scale.x, this.scale.y, this.scale.z],", + " octaves: this.octaves,", + " velocityMultiplier: [this.velocityMultiplier.x, this.velocityMultiplier.y, this.velocityMultiplier.z],", + " timeScale: [this.timeScale.x, this.timeScale.y, this.timeScale.z],", + " };", + " }", + " frameUpdate(delta) {", + " this.timeOffset.x += delta * this.timeScale.x;", + " this.timeOffset.y += delta * this.timeScale.y;", + " this.timeOffset.z += delta * this.timeScale.z;", + " }", + " static fromJSON(json) {", + " return new TurbulenceField(new THREE.Vector3(json.scale[0], json.scale[1], json.scale[2]), json.octaves, new THREE.Vector3(json.velocityMultiplier[0], json.velocityMultiplier[1], json.velocityMultiplier[2]), new THREE.Vector3(json.timeScale[0], json.timeScale[1], json.timeScale[2]));", + " }", + " clone() {", + " return new TurbulenceField(this.scale.clone(), this.octaves, this.velocityMultiplier.clone(), this.timeScale.clone());", + " }", + " reset() {", + " }", + "}", + "", + "function randomInt(a, b) {", + " return Math.floor(Math.random() * (b - a)) + a;", + "}", + "", + "const generators = [];", + "const tempV$2 = new THREE.Vector3();", + "const tempQ$2 = new THREE.Quaternion();", + "class Noise {", + " constructor(frequency, power, positionAmount = new ConstantValue(1), rotationAmount = new ConstantValue(0)) {", + " this.frequency = frequency;", + " this.power = power;", + " this.positionAmount = positionAmount;", + " this.rotationAmount = rotationAmount;", + " this.type = 'Noise';", + " this.duration = 0;", + " if (generators.length === 0) {", + " for (let i = 0; i < 100; i++) {", + " generators.push(new SimplexNoise());", + " }", + " }", + " }", + " initialize(particle) {", + " particle.lastPosNoise = new THREE.Vector3();", + " if (typeof particle.rotation === 'number') {", + " particle.lastRotNoise = 0;", + " }", + " else {", + " particle.lastRotNoise = new THREE.Quaternion();", + " }", + " particle.generatorIndex = [randomInt(0, 100), randomInt(0, 100), randomInt(0, 100), randomInt(0, 100)];", + " }", + " update(particle, _) {", + " let frequency = this.frequency.genValue(particle.age / particle.life);", + " let power = this.power.genValue(particle.age / particle.life);", + " let positionAmount = this.positionAmount.genValue(particle.age / particle.life);", + " let rotationAmount = this.rotationAmount.genValue(particle.age / particle.life);", + " if (positionAmount > 0 && particle.lastPosNoise !== undefined) {", + " particle.position.sub(particle.lastPosNoise);", + " tempV$2.set(generators[particle.generatorIndex[0]].noise2D(0, particle.age * frequency) *", + " power *", + " positionAmount, generators[particle.generatorIndex[1]].noise2D(0, particle.age * frequency) *", + " power *", + " positionAmount, generators[particle.generatorIndex[2]].noise2D(0, particle.age * frequency) *", + " power *", + " positionAmount);", + " particle.position.add(tempV$2);", + " particle.lastPosNoise.copy(tempV$2);", + " }", + " if (rotationAmount > 0 && particle.lastRotNoise !== undefined) {", + " if (typeof particle.rotation === 'number') {", + " particle.rotation -= particle.lastRotNoise;", + " particle.rotation +=", + " generators[particle.generatorIndex[3]].noise2D(0, particle.age * frequency) *", + " Math.PI *", + " power *", + " rotationAmount;", + " }", + " else {", + " particle.lastRotNoise.invert();", + " particle.rotation.multiply(particle.lastRotNoise);", + " tempQ$2", + " .set(generators[particle.generatorIndex[0]].noise2D(0, particle.age * frequency) *", + " power *", + " rotationAmount, generators[particle.generatorIndex[1]].noise2D(0, particle.age * frequency) *", + " power *", + " rotationAmount, generators[particle.generatorIndex[2]].noise2D(0, particle.age * frequency) *", + " power *", + " rotationAmount, generators[particle.generatorIndex[3]].noise2D(0, particle.age * frequency) *", + " power *", + " rotationAmount)", + " .normalize();", + " particle.rotation.multiply(tempQ$2);", + " particle.lastRotNoise.copy(tempQ$2);", + " }", + " }", + " }", + " toJSON() {", + " return {", + " type: this.type,", + " frequency: this.frequency.toJSON(),", + " power: this.power.toJSON(),", + " positionAmount: this.positionAmount.toJSON(),", + " rotationAmount: this.rotationAmount.toJSON(),", + " };", + " }", + " frameUpdate(delta) {", + " this.duration += delta;", + " }", + " static fromJSON(json) {", + " return new Noise(ValueGeneratorFromJSON(json.frequency), ValueGeneratorFromJSON(json.power), ValueGeneratorFromJSON(json.positionAmount), ValueGeneratorFromJSON(json.rotationAmount));", + " }", + " clone() {", + " return new Noise(this.frequency.clone(), this.power.clone(), this.positionAmount.clone(), this.rotationAmount.clone());", + " }", + " reset() { }", + "}", + "", + "class TextureSequencer {", + " constructor(scaleX = 0, scaleY = 0, position = new THREE.Vector3()) {", + " this.scaleX = scaleX;", + " this.scaleY = scaleY;", + " this.position = position;", + " this.locations = [];", + " }", + " transform(position, index) {", + " position.x = this.locations[index % this.locations.length].x * this.scaleX + this.position.x;", + " position.y = this.locations[index % this.locations.length].y * this.scaleY + this.position.y;", + " position.z = this.position.z;", + " }", + " static fromJSON(json) {", + " const textureSequencer = new TextureSequencer(json.scaleX, json.scaleY, new THREE.Vector3(json.position[0], json.position[1], json.position[2]));", + " textureSequencer.locations = json.locations.map((loc) => new THREE.Vector2(loc.x, loc.y));", + " return textureSequencer;", + " }", + " clone() {", + " const textureSequencer = new TextureSequencer(this.scaleX, this.scaleY, this.position.clone());", + " textureSequencer.locations = this.locations.map(loc => loc.clone());", + " return textureSequencer;", + " }", + " toJSON() {", + " return {", + " scaleX: this.scaleX,", + " scaleY: this.scaleY,", + " position: this.position,", + " locations: this.locations.map(loc => ({", + " x: loc.x,", + " y: loc.y,", + " }))", + " };", + " }", + " fromImage(img, threshold) {", + " const canvas = document.createElement(\"canvas\");", + " canvas.width = img.width;", + " canvas.height = img.height;", + " const ctx = canvas.getContext(\"2d\");", + " if (!ctx) {", + " return;", + " }", + " ctx.drawImage(img, 0, 0);", + " const data = ctx.getImageData(0, 0, canvas.width, canvas.height, { colorSpace: \"srgb\" });", + " canvas.remove();", + " this.locations.length = 0;", + " for (let i = 0; i < data.height; i++) {", + " for (let j = 0; j < data.width; j++) {", + " if (data.data[(i * data.width + j) * 4 + 3] > threshold) {", + " this.locations.push(new THREE.Vector2(j, data.height - i));", + " }", + " }", + " }", + " }", + "}", + "", + "function SequencerFromJSON(json) {", + " switch (json.type) {", + " case 'TextureSequencer':", + " return TextureSequencer.fromJSON(json);", + " default:", + " return new TextureSequencer();", + " }", + "}", + "", + "class ApplySequences {", + " constructor(delayBetweenParticles) {", + " this.type = 'ApplySequences';", + " this.sequencers = [];", + " this.time = 0;", + " this.index = 0;", + " this.pCount = 0;", + " this.tempV = new THREE.Vector3();", + " this.delay = delayBetweenParticles;", + " }", + " initialize(particle) {", + " particle.id = this.pCount;", + " particle.dst = new THREE.Vector3();", + " particle.begin = new THREE.Vector3();", + " particle.inMotion = false;", + " this.pCount++;", + " }", + " reset() {", + " this.time = 0;", + " this.index = 0;", + " this.pCount = 0;", + " }", + " update(particle, delta) {", + " const sequencer = this.sequencers[this.index];", + " const delay = particle.id * this.delay;", + " if (this.time >= sequencer[0].a + delay && this.time <= sequencer[0].b + delay) {", + " if (!particle.inMotion) {", + " particle.inMotion = true;", + " particle.begin.copy(particle.position);", + " sequencer[1].transform(particle.dst, particle.id);", + " }", + " particle.position.lerpVectors(particle.begin, particle.dst, ApplySequences.BEZIER.genValue((this.time - sequencer[0].a - delay) / (sequencer[0].b - sequencer[0].a)));", + " }", + " else if (this.time > sequencer[0].b + delay) {", + " particle.inMotion = false;", + " }", + " }", + " frameUpdate(delta) {", + " while (this.index + 1 < this.sequencers.length && this.time >= this.sequencers[this.index + 1][0].a) {", + " this.index++;", + " }", + " this.time += delta;", + " }", + " appendSequencer(range, sequencer) {", + " this.sequencers.push([range, sequencer]);", + " }", + " toJSON() {", + " return {", + " type: this.type,", + " delay: this.delay,", + " sequencers: this.sequencers.map(([range, sequencer]) => ({", + " range: range.toJSON(),", + " sequencer: sequencer.toJSON(),", + " })),", + " };", + " }", + " static fromJSON(json) {", + " const seq = new ApplySequences(json.delay);", + " json.sequencers.forEach((sequencerJson) => {", + " seq.sequencers.push([ValueGeneratorFromJSON(sequencerJson.range), SequencerFromJSON(sequencerJson.sequencer)]);", + " });", + " return seq;", + " }", + " clone() {", + " const applySequences = new ApplySequences(this.delay);", + " applySequences.sequencers = this.sequencers.map(seq => [seq[0].clone(), seq[1].clone()]);", + " return applySequences;", + " }", + "}", + "ApplySequences.BEZIER = new Bezier(0, 0, 1, 1);", + "", + "let physicsResolver;", + "function setPhysicsResolver(resolver) {", + " physicsResolver = resolver;", + "}", + "function getPhysicsResolver() {", + " return physicsResolver;", + "}", + "class ApplyCollision {", + " constructor(resolver, bounce) {", + " this.resolver = resolver;", + " this.bounce = bounce;", + " this.type = 'ApplyCollision';", + " this.tempV = new THREE.Vector3();", + " }", + " initialize(particle) {", + " }", + " update(particle, delta) {", + " if (this.resolver.resolve(particle.position, this.tempV)) {", + " particle.velocity.reflect(this.tempV).multiplyScalar(this.bounce);", + " }", + " }", + " frameUpdate(delta) {", + " }", + " toJSON() {", + " return {", + " type: this.type,", + " bounce: this.bounce,", + " };", + " }", + " static fromJSON(json) {", + " return new ApplyCollision(getPhysicsResolver(), json.bounce);", + " }", + " clone() {", + " return new ApplyCollision(this.resolver, this.bounce);", + " }", + " reset() {", + " }", + "}", + "", + "class ColorBySpeed {", + " constructor(color, speedRange) {", + " this.color = color;", + " this.speedRange = speedRange;", + " this.type = 'ColorBySpeed';", + " }", + " initialize(particle) {", + " if (this.color.type === 'memorizedFunction') {", + " particle.colorOverLifeMemory = {};", + " this.color.startGen(particle.colorOverLifeMemory);", + " }", + " }", + " update(particle, delta) {", + " const t = (particle.startSpeed - this.speedRange.a) / (this.speedRange.b - this.speedRange.a);", + " if (this.color.type === 'memorizedFunction') {", + " this.color.genColor(particle.color, t, particle.colorOverLifeMemory);", + " }", + " else {", + " this.color.genColor(particle.color, t);", + " }", + " particle.color.x *= particle.startColor.x;", + " particle.color.y *= particle.startColor.y;", + " particle.color.z *= particle.startColor.z;", + " particle.color.w *= particle.startColor.w;", + " }", + " frameUpdate(delta) { }", + " toJSON() {", + " return {", + " type: this.type,", + " color: this.color.toJSON(),", + " speedRange: this.speedRange.toJSON(),", + " };", + " }", + " static fromJSON(json) {", + " return new ColorBySpeed(ColorGeneratorFromJSON(json.color), IntervalValue.fromJSON(json.speedRange));", + " }", + " clone() {", + " return new ColorBySpeed(this.color.clone(), this.speedRange.clone());", + " }", + " reset() { }", + "}", + "", + "class SizeBySpeed {", + " initialize(particle) { }", + " constructor(size, speedRange) {", + " this.size = size;", + " this.speedRange = speedRange;", + " this.type = 'SizeBySpeed';", + " }", + " update(particle) {", + " const t = (particle.startSpeed - this.speedRange.a) / (this.speedRange.b - this.speedRange.a);", + " particle.size = particle.startSize * this.size.genValue(t);", + " }", + " toJSON() {", + " return {", + " type: this.type,", + " size: this.size.toJSON(),", + " speedRange: this.speedRange.toJSON(),", + " };", + " }", + " static fromJSON(json) {", + " return new SizeBySpeed(ValueGeneratorFromJSON(json.size), IntervalValue.fromJSON(json.speedRange));", + " }", + " frameUpdate(delta) { }", + " clone() {", + " return new SizeBySpeed(this.size.clone(), this.speedRange.clone());", + " }", + " reset() { }", + "}", + "", + "class RotationBySpeed {", + " constructor(angularVelocity, speedRange) {", + " this.angularVelocity = angularVelocity;", + " this.speedRange = speedRange;", + " this.type = 'RotationBySpeed';", + " this.tempQuat = new THREE.Quaternion();", + " }", + " initialize(particle) { }", + " update(particle, delta) {", + " const t = (particle.startSpeed - this.speedRange.a) / (this.speedRange.b - this.speedRange.a);", + " particle.rotation += delta * this.angularVelocity.genValue(t);", + " }", + " toJSON() {", + " return {", + " type: this.type,", + " angularVelocity: this.angularVelocity.toJSON(),", + " speedRange: this.speedRange.toJSON(),", + " };", + " }", + " static fromJSON(json) {", + " return new RotationBySpeed(ValueGeneratorFromJSON(json.angularVelocity), IntervalValue.fromJSON(json.speedRange));", + " }", + " frameUpdate(delta) { }", + " clone() {", + " return new RotationBySpeed(this.angularVelocity.clone(), this.speedRange.clone());", + " }", + " reset() { }", + "}", + "", + "class LimitSpeedOverLife {", + " initialize(particle) { }", + " constructor(speed, dampen) {", + " this.speed = speed;", + " this.dampen = dampen;", + " this.type = 'LimitSpeedOverLife';", + " }", + " update(particle, delta) {", + " let speed = particle.velocity.length();", + " let limit = this.speed.genValue(particle.age / particle.life);", + " if (speed > limit) {", + " const percent = (speed - limit) / speed;", + " particle.velocity.multiplyScalar(1 - percent * this.dampen * delta * 20);", + " }", + " }", + " toJSON() {", + " return {", + " type: this.type,", + " speed: this.speed.toJSON(),", + " dampen: this.dampen,", + " };", + " }", + " static fromJSON(json) {", + " return new LimitSpeedOverLife(ValueGeneratorFromJSON(json.speed), json.dampen);", + " }", + " frameUpdate(delta) { }", + " clone() {", + " return new LimitSpeedOverLife(this.speed.clone(), this.dampen);", + " }", + " reset() { }", + "}", + "", + "const BehaviorTypes = {", + " ApplyForce: {", + " type: 'ApplyForce',", + " constructor: ApplyForce,", + " params: [", + " ['direction', 'vec3'],", + " ['magnitude', 'value'],", + " ],", + " loadJSON: ApplyForce.fromJSON,", + " },", + " Noise: {", + " type: 'Noise',", + " constructor: Noise,", + " params: [", + " ['frequency', 'value'],", + " ['power', 'value'],", + " ['positionAmount', 'value'],", + " ['rotationAmount', 'value'],", + " ],", + " loadJSON: Noise.fromJSON,", + " },", + " TurbulenceField: {", + " type: 'TurbulenceField',", + " constructor: TurbulenceField,", + " params: [", + " ['scale', 'vec3'],", + " ['octaves', 'number'],", + " ['velocityMultiplier', 'vec3'],", + " ['timeScale', 'vec3'],", + " ],", + " loadJSON: TurbulenceField.fromJSON,", + " },", + " GravityForce: {", + " type: 'GravityForce',", + " constructor: GravityForce,", + " params: [", + " ['center', 'vec3'],", + " ['magnitude', 'number'],", + " ],", + " loadJSON: GravityForce.fromJSON,", + " },", + " ColorOverLife: {", + " type: 'ColorOverLife',", + " constructor: ColorOverLife,", + " params: [['color', 'colorFunc']],", + " loadJSON: ColorOverLife.fromJSON,", + " },", + " RotationOverLife: {", + " type: 'RotationOverLife',", + " constructor: RotationOverLife,", + " params: [['angularVelocity', 'valueFunc']],", + " loadJSON: RotationOverLife.fromJSON,", + " },", + " Rotation3DOverLife: {", + " type: 'Rotation3DOverLife',", + " constructor: Rotation3DOverLife,", + " params: [['angularVelocity', 'rotationFunc']],", + " loadJSON: Rotation3DOverLife.fromJSON,", + " },", + " SizeOverLife: {", + " type: 'SizeOverLife',", + " constructor: SizeOverLife,", + " params: [['size', 'valueFunc']],", + " loadJSON: SizeOverLife.fromJSON,", + " },", + " ColorBySpeed: {", + " type: 'ColorBySpeed',", + " constructor: ColorBySpeed,", + " params: [", + " ['color', 'colorFunc'],", + " ['speedRange', 'range'],", + " ],", + " loadJSON: ColorBySpeed.fromJSON,", + " },", + " RotationBySpeed: {", + " type: 'RotationBySpeed',", + " constructor: RotationBySpeed,", + " params: [", + " ['angularVelocity', 'valueFunc'],", + " ['speedRange', 'range'],", + " ],", + " loadJSON: RotationBySpeed.fromJSON,", + " },", + " SizeBySpeed: {", + " type: 'SizeBySpeed',", + " constructor: SizeBySpeed,", + " params: [", + " ['size', 'valueFunc'],", + " ['speedRange', 'range'],", + " ],", + " loadJSON: SizeBySpeed.fromJSON,", + " },", + " SpeedOverLife: {", + " type: 'SpeedOverLife',", + " constructor: SpeedOverLife,", + " params: [['speed', 'valueFunc']],", + " loadJSON: SpeedOverLife.fromJSON,", + " },", + " FrameOverLife: {", + " type: 'FrameOverLife',", + " constructor: FrameOverLife,", + " params: [['frame', 'valueFunc']],", + " loadJSON: FrameOverLife.fromJSON,", + " },", + " ForceOverLife: {", + " type: 'ForceOverLife',", + " constructor: ForceOverLife,", + " params: [", + " ['x', 'valueFunc'],", + " ['y', 'valueFunc'],", + " ['z', 'valueFunc'],", + " ],", + " loadJSON: ForceOverLife.fromJSON,", + " },", + " OrbitOverLife: {", + " type: 'OrbitOverLife',", + " constructor: OrbitOverLife,", + " params: [", + " ['orbitSpeed', 'valueFunc'],", + " ['axis', 'vec3'],", + " ],", + " loadJSON: OrbitOverLife.fromJSON,", + " },", + " WidthOverLength: {", + " type: 'WidthOverLength',", + " constructor: WidthOverLength,", + " params: [['width', 'valueFunc']],", + " loadJSON: WidthOverLength.fromJSON,", + " },", + " ChangeEmitDirection: {", + " type: 'ChangeEmitDirection',", + " constructor: ChangeEmitDirection,", + " params: [['angle', 'value']],", + " loadJSON: ChangeEmitDirection.fromJSON,", + " },", + " EmitSubParticleSystem: {", + " type: 'EmitSubParticleSystem',", + " constructor: EmitSubParticleSystem,", + " params: [", + " ['particleSystem', 'self'],", + " ['useVelocityAsBasis', 'boolean'],", + " ['subParticleSystem', 'particleSystem'],", + " ['mode', 'number'],", + " ['emitProbability', 'number'],", + " ],", + " loadJSON: EmitSubParticleSystem.fromJSON,", + " },", + " LimitSpeedOverLife: {", + " type: 'LimitSpeedOverLife',", + " constructor: LimitSpeedOverLife,", + " params: [", + " ['speed', 'valueFunc'],", + " ['dampen', 'number'],", + " ],", + " loadJSON: LimitSpeedOverLife.fromJSON,", + " },", + "};", + "function BehaviorFromJSON(json, particleSystem) {", + " return BehaviorTypes[json.type].loadJSON(json, particleSystem);", + "}", + "", + "let EmitterMode = void 0;", + "(function (EmitterMode) {", + " EmitterMode[EmitterMode[\"Random\"] = 0] = \"Random\";", + " EmitterMode[EmitterMode[\"Loop\"] = 1] = \"Loop\";", + " EmitterMode[EmitterMode[\"PingPong\"] = 2] = \"PingPong\";", + " EmitterMode[EmitterMode[\"Burst\"] = 3] = \"Burst\";", + "})(EmitterMode || (EmitterMode = {}));", + "function getValueFromEmitterMode(mode, currentValue, spread) {", + " let u;", + " if (EmitterMode.Random === mode) {", + " currentValue = Math.random();", + " }", + " if (spread > 0) {", + " u = Math.floor(currentValue / spread) * spread;", + " }", + " else {", + " u = currentValue;", + " }", + " switch (mode) {", + " case EmitterMode.Loop:", + " u = u % 1;", + " break;", + " case EmitterMode.PingPong:", + " u = Math.abs((u % 2) - 1);", + " break;", + " }", + " return u;", + "}", + "", + "class ConeEmitter {", + " constructor(parameters = {}) {", + " var _a, _b, _c, _d, _e, _f, _g;", + " this.type = 'cone';", + " this.currentValue = 0;", + " this.radius = (_a = parameters.radius) !== null && _a !== void 0 ? _a : 10;", + " this.arc = (_b = parameters.arc) !== null && _b !== void 0 ? _b : 2.0 * Math.PI;", + " this.thickness = (_c = parameters.thickness) !== null && _c !== void 0 ? _c : 1;", + " this.angle = (_d = parameters.angle) !== null && _d !== void 0 ? _d : Math.PI / 6;", + " this.mode = (_e = parameters.mode) !== null && _e !== void 0 ? _e : EmitterMode.Random;", + " this.spread = (_f = parameters.spread) !== null && _f !== void 0 ? _f : 0;", + " this.speed = (_g = parameters.speed) !== null && _g !== void 0 ? _g : new ConstantValue(1);", + " }", + " update(system, delta) {", + " if (EmitterMode.Random != this.mode) {", + " this.currentValue += this.speed.genValue(system.emissionState.time / system.duration) * delta;", + " }", + " }", + " initialize(p) {", + " const u = getValueFromEmitterMode(this.mode, this.currentValue, this.spread);", + " const rand = THREE.MathUtils.lerp(1 - this.thickness, 1, Math.random());", + " const theta = u * this.arc;", + " const r = Math.sqrt(rand);", + " const sinTheta = Math.sin(theta);", + " const cosTheta = Math.cos(theta);", + " p.position.x = r * cosTheta;", + " p.position.y = r * sinTheta;", + " p.position.z = 0;", + " const angle = this.angle * r;", + " p.velocity.set(0, 0, Math.cos(angle)).addScaledVector(p.position, Math.sin(angle)).multiplyScalar(p.startSpeed);", + " p.position.multiplyScalar(this.radius);", + " }", + " toJSON() {", + " return {", + " type: 'cone',", + " radius: this.radius,", + " arc: this.arc,", + " thickness: this.thickness,", + " angle: this.angle,", + " mode: this.mode,", + " spread: this.spread,", + " speed: this.speed.toJSON(),", + " };", + " }", + " static fromJSON(json) {", + " return new ConeEmitter({", + " radius: json.radius,", + " arc: json.arc,", + " thickness: json.thickness,", + " angle: json.angle,", + " mode: json.mode,", + " speed: json.speed ? ValueGeneratorFromJSON(json.speed) : undefined,", + " spread: json.spread,", + " });", + " }", + " clone() {", + " return new ConeEmitter({", + " radius: this.radius,", + " arc: this.arc,", + " thickness: this.thickness,", + " angle: this.angle,", + " mode: this.mode,", + " speed: this.speed.clone(),", + " spread: this.spread,", + " });", + " }", + "}", + "", + "class CircleEmitter {", + " constructor(parameters = {}) {", + " var _a, _b, _c, _d, _e, _f;", + " this.type = 'circle';", + " this.currentValue = 0;", + " this.radius = (_a = parameters.radius) !== null && _a !== void 0 ? _a : 10;", + " this.arc = (_b = parameters.arc) !== null && _b !== void 0 ? _b : 2.0 * Math.PI;", + " this.thickness = (_c = parameters.thickness) !== null && _c !== void 0 ? _c : 1;", + " this.mode = (_d = parameters.mode) !== null && _d !== void 0 ? _d : EmitterMode.Random;", + " this.spread = (_e = parameters.spread) !== null && _e !== void 0 ? _e : 0;", + " this.speed = (_f = parameters.speed) !== null && _f !== void 0 ? _f : new ConstantValue(1);", + " }", + " update(system, delta) {", + " this.currentValue += this.speed.genValue(system.emissionState.time / system.duration) * delta;", + " }", + " initialize(p) {", + " const u = getValueFromEmitterMode(this.mode, this.currentValue, this.spread);", + " const r = THREE.MathUtils.lerp(1 - this.thickness, 1, Math.random());", + " const theta = u * this.arc;", + " p.position.x = Math.cos(theta);", + " p.position.y = Math.sin(theta);", + " p.position.z = 0;", + " p.velocity.copy(p.position).multiplyScalar(p.startSpeed);", + " p.position.multiplyScalar(this.radius * r);", + " }", + " toJSON() {", + " return {", + " type: 'circle',", + " radius: this.radius,", + " arc: this.arc,", + " thickness: this.thickness,", + " mode: this.mode,", + " spread: this.spread,", + " speed: this.speed.toJSON(),", + " };", + " }", + " static fromJSON(json) {", + " return new CircleEmitter({", + " radius: json.radius,", + " arc: json.arc,", + " thickness: json.thickness,", + " mode: json.mode,", + " speed: json.speed ? ValueGeneratorFromJSON(json.speed) : undefined,", + " spread: json.spread,", + " });", + " }", + " clone() {", + " return new CircleEmitter({", + " radius: this.radius,", + " arc: this.arc,", + " thickness: this.thickness,", + " mode: this.mode,", + " speed: this.speed.clone(),", + " spread: this.spread,", + " });", + " }", + "}", + "", + "class DonutEmitter {", + " constructor(parameters = {}) {", + " var _a, _b, _c, _d, _e, _f, _g;", + " this.type = 'donut';", + " this.currentValue = 0;", + " this.radius = (_a = parameters.radius) !== null && _a !== void 0 ? _a : 10;", + " this.arc = (_b = parameters.arc) !== null && _b !== void 0 ? _b : 2.0 * Math.PI;", + " this.thickness = (_c = parameters.thickness) !== null && _c !== void 0 ? _c : 1;", + " this.donutRadius = (_d = parameters.donutRadius) !== null && _d !== void 0 ? _d : this.radius * 0.2;", + " this.mode = (_e = parameters.mode) !== null && _e !== void 0 ? _e : EmitterMode.Random;", + " this.spread = (_f = parameters.spread) !== null && _f !== void 0 ? _f : 0;", + " this.speed = (_g = parameters.speed) !== null && _g !== void 0 ? _g : new ConstantValue(1);", + " }", + " update(system, delta) {", + " if (EmitterMode.Random != this.mode) {", + " this.currentValue += this.speed.genValue(system.emissionState.time / system.duration) * delta;", + " }", + " }", + " initialize(p) {", + " const u = getValueFromEmitterMode(this.mode, this.currentValue, this.spread);", + " const v = Math.random();", + " const rand = THREE.MathUtils.lerp(1 - this.thickness, 1, Math.random());", + " const theta = u * this.arc;", + " const phi = v * Math.PI * 2;", + " const sinTheta = Math.sin(theta);", + " const cosTheta = Math.cos(theta);", + " p.position.x = this.radius * cosTheta;", + " p.position.y = this.radius * sinTheta;", + " p.position.z = 0;", + " p.velocity.z = this.donutRadius * rand * Math.sin(phi);", + " p.velocity.x = this.donutRadius * rand * Math.cos(phi) * cosTheta;", + " p.velocity.y = this.donutRadius * rand * Math.cos(phi) * sinTheta;", + " p.position.add(p.velocity);", + " p.velocity.normalize().multiplyScalar(p.startSpeed);", + " }", + " toJSON() {", + " return {", + " type: 'donut',", + " radius: this.radius,", + " arc: this.arc,", + " thickness: this.thickness,", + " donutRadius: this.donutRadius,", + " mode: this.mode,", + " spread: this.spread,", + " speed: this.speed.toJSON(),", + " };", + " }", + " static fromJSON(json) {", + " return new DonutEmitter({", + " radius: json.radius,", + " arc: json.arc,", + " thickness: json.thickness,", + " donutRadius: json.donutRadius,", + " mode: json.mode,", + " speed: json.speed ? ValueGeneratorFromJSON(json.speed) : undefined,", + " spread: json.spread,", + " });", + " }", + " clone() {", + " return new DonutEmitter({", + " radius: this.radius,", + " arc: this.arc,", + " thickness: this.thickness,", + " donutRadius: this.donutRadius,", + " mode: this.mode,", + " speed: this.speed.clone(),", + " spread: this.spread,", + " });", + " }", + "}", + "", + "class PointEmitter {", + " constructor() {", + " this.type = 'point';", + " }", + " update(system, delta) { }", + " initialize(p) {", + " const u = Math.random();", + " const v = Math.random();", + " const theta = u * Math.PI * 2;", + " const phi = Math.acos(2.0 * v - 1.0);", + " const r = Math.cbrt(Math.random());", + " const sinTheta = Math.sin(theta);", + " const cosTheta = Math.cos(theta);", + " const sinPhi = Math.sin(phi);", + " const cosPhi = Math.cos(phi);", + " p.velocity.x = r * sinPhi * cosTheta;", + " p.velocity.y = r * sinPhi * sinTheta;", + " p.velocity.z = r * cosPhi;", + " p.velocity.multiplyScalar(p.startSpeed);", + " p.position.setScalar(0);", + " }", + " toJSON() {", + " return {", + " type: 'point',", + " };", + " }", + " static fromJSON(json) {", + " return new PointEmitter();", + " }", + " clone() {", + " return new PointEmitter();", + " }", + "}", + "", + "class SphereEmitter {", + " constructor(parameters = {}) {", + " var _a, _b, _c, _d, _e, _f;", + " this.type = 'sphere';", + " this.currentValue = 0;", + " this.radius = (_a = parameters.radius) !== null && _a !== void 0 ? _a : 10;", + " this.arc = (_b = parameters.arc) !== null && _b !== void 0 ? _b : 2.0 * Math.PI;", + " this.thickness = (_c = parameters.thickness) !== null && _c !== void 0 ? _c : 1;", + " this.mode = (_d = parameters.mode) !== null && _d !== void 0 ? _d : EmitterMode.Random;", + " this.spread = (_e = parameters.spread) !== null && _e !== void 0 ? _e : 0;", + " this.speed = (_f = parameters.speed) !== null && _f !== void 0 ? _f : new ConstantValue(1);", + " }", + " update(system, delta) {", + " if (EmitterMode.Random != this.mode) {", + " this.currentValue += this.speed.genValue(system.emissionState.time / system.duration) * delta;", + " }", + " }", + " initialize(p) {", + " const u = getValueFromEmitterMode(this.mode, this.currentValue, this.spread);", + " const v = Math.random();", + " const rand = THREE.MathUtils.lerp(1 - this.thickness, 1, Math.random());", + " const theta = u * this.arc;", + " const phi = Math.acos(2.0 * v - 1.0);", + " const sinTheta = Math.sin(theta);", + " const cosTheta = Math.cos(theta);", + " const sinPhi = Math.sin(phi);", + " const cosPhi = Math.cos(phi);", + " p.position.x = sinPhi * cosTheta;", + " p.position.y = sinPhi * sinTheta;", + " p.position.z = cosPhi;", + " p.velocity.copy(p.position).multiplyScalar(p.startSpeed);", + " p.position.multiplyScalar(this.radius * rand);", + " }", + " toJSON() {", + " return {", + " type: 'sphere',", + " radius: this.radius,", + " arc: this.arc,", + " thickness: this.thickness,", + " mode: this.mode,", + " spread: this.spread,", + " speed: this.speed.toJSON(),", + " };", + " }", + " static fromJSON(json) {", + " return new SphereEmitter({", + " radius: json.radius,", + " arc: json.arc,", + " thickness: json.thickness,", + " mode: json.mode,", + " speed: json.speed ? ValueGeneratorFromJSON(json.speed) : undefined,", + " spread: json.spread,", + " });", + " }", + " clone() {", + " return new SphereEmitter({", + " radius: this.radius,", + " arc: this.arc,", + " thickness: this.thickness,", + " mode: this.mode,", + " speed: this.speed.clone(),", + " spread: this.spread,", + " });", + " }", + "}", + "", + "class HemisphereEmitter {", + " constructor(parameters = {}) {", + " var _a, _b, _c, _d, _e, _f;", + " this.type = 'sphere';", + " this.currentValue = 0;", + " this.radius = (_a = parameters.radius) !== null && _a !== void 0 ? _a : 10;", + " this.arc = (_b = parameters.arc) !== null && _b !== void 0 ? _b : 2.0 * Math.PI;", + " this.thickness = (_c = parameters.thickness) !== null && _c !== void 0 ? _c : 1;", + " this.mode = (_d = parameters.mode) !== null && _d !== void 0 ? _d : EmitterMode.Random;", + " this.spread = (_e = parameters.spread) !== null && _e !== void 0 ? _e : 0;", + " this.speed = (_f = parameters.speed) !== null && _f !== void 0 ? _f : new ConstantValue(1);", + " }", + " update(system, delta) {", + " if (EmitterMode.Random != this.mode) {", + " this.currentValue += this.speed.genValue(system.emissionState.time / system.duration) * delta;", + " }", + " }", + " initialize(p) {", + " const u = getValueFromEmitterMode(this.mode, this.currentValue, this.spread);", + " const v = Math.random();", + " const rand = THREE.MathUtils.lerp(1 - this.thickness, 1, Math.random());", + " const theta = u * this.arc;", + " const phi = Math.acos(v);", + " const sinTheta = Math.sin(theta);", + " const cosTheta = Math.cos(theta);", + " const sinPhi = Math.sin(phi);", + " const cosPhi = Math.cos(phi);", + " p.position.x = sinPhi * cosTheta;", + " p.position.y = sinPhi * sinTheta;", + " p.position.z = cosPhi;", + " p.velocity.copy(p.position).multiplyScalar(p.startSpeed);", + " p.position.multiplyScalar(this.radius * rand);", + " }", + " toJSON() {", + " return {", + " type: 'hemisphere',", + " radius: this.radius,", + " arc: this.arc,", + " thickness: this.thickness,", + " mode: this.mode,", + " spread: this.spread,", + " speed: this.speed.toJSON(),", + " };", + " }", + " static fromJSON(json) {", + " return new HemisphereEmitter({", + " radius: json.radius,", + " arc: json.arc,", + " thickness: json.thickness,", + " mode: json.mode,", + " speed: json.speed ? ValueGeneratorFromJSON(json.speed) : undefined,", + " spread: json.spread,", + " });", + " }", + " clone() {", + " return new HemisphereEmitter({", + " radius: this.radius,", + " arc: this.arc,", + " thickness: this.thickness,", + " mode: this.mode,", + " speed: this.speed.clone(),", + " spread: this.spread,", + " });", + " }", + "}", + "", + "class GridEmitter {", + " constructor(parameters = {}) {", + " var _a, _b, _c, _d;", + " this.type = 'grid';", + " this.width = (_a = parameters.width) !== null && _a !== void 0 ? _a : 1;", + " this.height = (_b = parameters.height) !== null && _b !== void 0 ? _b : 1;", + " this.column = (_c = parameters.column) !== null && _c !== void 0 ? _c : 10;", + " this.row = (_d = parameters.row) !== null && _d !== void 0 ? _d : 10;", + " }", + " initialize(p) {", + " const r = Math.floor(Math.random() * this.row);", + " const c = Math.floor(Math.random() * this.column);", + " p.position.x = (c * this.width) / this.column - this.width / 2;", + " p.position.y = (r * this.height) / this.row - this.height / 2;", + " p.position.z = 0;", + " p.velocity.set(0, 0, p.startSpeed);", + " }", + " toJSON() {", + " return {", + " type: 'grid',", + " width: this.width,", + " height: this.height,", + " column: this.column,", + " row: this.row,", + " };", + " }", + " static fromJSON(json) {", + " return new GridEmitter(json);", + " }", + " clone() {", + " return new GridEmitter({", + " width: this.width,", + " height: this.height,", + " column: this.column,", + " row: this.row,", + " });", + " }", + " update(system, delta) { }", + "}", + "", + "class MeshSurfaceEmitter {", + " get geometry() {", + " return this._geometry;", + " }", + " set geometry(geometry) {", + " this._geometry = geometry;", + " if (geometry === undefined) {", + " return;", + " }", + " if (typeof geometry === 'string') {", + " return;", + " }", + " const tri = new THREE.Triangle();", + " this._triangleIndexToArea.length = 0;", + " let area = 0;", + " if (!geometry.getIndex()) {", + " return;", + " }", + " const array = geometry.getIndex().array;", + " const triCount = array.length / 3;", + " this._triangleIndexToArea.push(0);", + " for (let i = 0; i < triCount; i++) {", + " tri.setFromAttributeAndIndices(geometry.getAttribute('position'), array[i * 3], array[i * 3 + 1], array[i * 3 + 2]);", + " area += tri.getArea();", + " this._triangleIndexToArea.push(area);", + " }", + " geometry.userData.triangleIndexToArea = this._triangleIndexToArea;", + " }", + " constructor(geometry) {", + " this.type = 'mesh_surface';", + " this._triangleIndexToArea = [];", + " this._tempA = new THREE.Vector3();", + " this._tempB = new THREE.Vector3();", + " this._tempC = new THREE.Vector3();", + " if (!geometry) {", + " return;", + " }", + " this.geometry = geometry;", + " }", + " initialize(p) {", + " const geometry = this._geometry;", + " if (!geometry || geometry.getIndex() === null) {", + " p.position.set(0, 0, 0);", + " p.velocity.set(0, 0, 1).multiplyScalar(p.startSpeed);", + " return;", + " }", + " const triCount = this._triangleIndexToArea.length - 1;", + " let left = 0, right = triCount;", + " const target = Math.random() * this._triangleIndexToArea[triCount];", + " while (left + 1 < right) {", + " const mid = Math.floor((left + right) / 2);", + " if (target < this._triangleIndexToArea[mid]) {", + " right = mid;", + " }", + " else {", + " left = mid;", + " }", + " }", + " let u1 = Math.random();", + " let u2 = Math.random();", + " if (u1 + u2 > 1) {", + " u1 = 1 - u1;", + " u2 = 1 - u2;", + " }", + " const index1 = geometry.getIndex().array[left * 3];", + " const index2 = geometry.getIndex().array[left * 3 + 1];", + " const index3 = geometry.getIndex().array[left * 3 + 2];", + " const positionBuffer = geometry.getAttribute('position');", + " this._tempA.fromBufferAttribute(positionBuffer, index1);", + " this._tempB.fromBufferAttribute(positionBuffer, index2);", + " this._tempC.fromBufferAttribute(positionBuffer, index3);", + " this._tempB.sub(this._tempA);", + " this._tempC.sub(this._tempA);", + " this._tempA.addScaledVector(this._tempB, u1).addScaledVector(this._tempC, u2);", + " p.position.copy(this._tempA);", + " this._tempA.copy(this._tempB).cross(this._tempC).normalize();", + " p.velocity.copy(this._tempA).normalize().multiplyScalar(p.startSpeed);", + " }", + " toJSON() {", + " return {", + " type: 'mesh_surface',", + " mesh: this._geometry ? this._geometry.uuid : '',", + " };", + " }", + " static fromJSON(json, meta) {", + " return new MeshSurfaceEmitter(meta.geometries[json.geometry]);", + " }", + " clone() {", + " return new MeshSurfaceEmitter(this._geometry);", + " }", + " update(system, delta) { }", + "}", + "", + "const EmitterShapes = {", + " circle: {", + " type: 'circle',", + " params: [", + " ['radius', 'number'],", + " ['arc', 'radian'],", + " ['thickness', 'number'],", + " ['mode', 'emitterMode'],", + " ['spread', 'number'],", + " ['speed', 'valueFunc'],", + " ],", + " constructor: CircleEmitter,", + " loadJSON: CircleEmitter.fromJSON,", + " },", + " cone: {", + " type: 'cone',", + " params: [", + " ['radius', 'number'],", + " ['arc', 'radian'],", + " ['thickness', 'number'],", + " ['angle', 'radian'],", + " ['mode', 'emitterMode'],", + " ['spread', 'number'],", + " ['speed', 'valueFunc'],", + " ],", + " constructor: ConeEmitter,", + " loadJSON: ConeEmitter.fromJSON,", + " },", + " donut: {", + " type: 'donut',", + " params: [", + " ['radius', 'number'],", + " ['arc', 'radian'],", + " ['thickness', 'number'],", + " ['donutRadius', 'number'],", + " ['mode', 'emitterMode'],", + " ['spread', 'number'],", + " ['speed', 'valueFunc'],", + " ],", + " constructor: DonutEmitter,", + " loadJSON: DonutEmitter.fromJSON,", + " },", + " point: { type: 'point', params: [], constructor: PointEmitter, loadJSON: PointEmitter.fromJSON },", + " sphere: {", + " type: 'sphere',", + " params: [", + " ['radius', 'number'],", + " ['arc', 'radian'],", + " ['thickness', 'number'],", + " ['angle', 'radian'],", + " ['mode', 'emitterMode'],", + " ['spread', 'number'],", + " ['speed', 'valueFunc'],", + " ],", + " constructor: SphereEmitter,", + " loadJSON: SphereEmitter.fromJSON,", + " },", + " hemisphere: {", + " type: 'hemisphere',", + " params: [", + " ['radius', 'number'],", + " ['arc', 'radian'],", + " ['thickness', 'number'],", + " ['angle', 'radian'],", + " ['mode', 'emitterMode'],", + " ['spread', 'number'],", + " ['speed', 'valueFunc'],", + " ],", + " constructor: HemisphereEmitter,", + " loadJSON: HemisphereEmitter.fromJSON,", + " },", + " grid: {", + " type: 'grid',", + " params: [", + " ['width', 'number'],", + " ['height', 'number'],", + " ['rows', 'number'],", + " ['column', 'number'],", + " ],", + " constructor: GridEmitter,", + " loadJSON: GridEmitter.fromJSON,", + " },", + " mesh_surface: {", + " type: 'mesh_surface',", + " params: [['geometry', 'geometry']],", + " constructor: MeshSurfaceEmitter,", + " loadJSON: MeshSurfaceEmitter.fromJSON,", + " },", + "};", + "function EmitterFromJSON(json, meta) {", + " return EmitterShapes[json.type].loadJSON(json, meta);", + "}", + "", + "let RenderMode = void 0;", + "(function (RenderMode) {", + " RenderMode[RenderMode[\"BillBoard\"] = 0] = \"BillBoard\";", + " RenderMode[RenderMode[\"StretchedBillBoard\"] = 1] = \"StretchedBillBoard\";", + " RenderMode[RenderMode[\"Mesh\"] = 2] = \"Mesh\";", + " RenderMode[RenderMode[\"Trail\"] = 3] = \"Trail\";", + " RenderMode[RenderMode[\"HorizontalBillBoard\"] = 4] = \"HorizontalBillBoard\";", + " RenderMode[RenderMode[\"VerticalBillBoard\"] = 5] = \"VerticalBillBoard\";", + "})(RenderMode || (RenderMode = {}));", + "class VFXBatch extends THREE.Mesh {", + " constructor(settings) {", + " super();", + " this.type = 'VFXBatch';", + " this.maxParticles = 1000;", + " this.systems = new Set();", + " const layers = new THREE.Layers();", + " layers.mask = settings.layers.mask;", + " const newMat = settings.material.clone();", + " newMat.defines = {};", + " Object.assign(newMat.defines, settings.material.defines);", + " this.settings = {", + " instancingGeometry: settings.instancingGeometry,", + " renderMode: settings.renderMode,", + " renderOrder: settings.renderOrder,", + " material: newMat,", + " uTileCount: settings.uTileCount,", + " vTileCount: settings.vTileCount,", + " layers: layers,", + " };", + " this.frustumCulled = false;", + " this.renderOrder = this.settings.renderOrder;", + " }", + " addSystem(system) {", + " this.systems.add(system);", + " }", + " removeSystem(system) {", + " this.systems.delete(system);", + " }", + "}", + "", + "const UP = new THREE.Vector3(0, 0, 1);", + "const tempQ$1 = new THREE.Quaternion();", + "const tempV$1 = new THREE.Vector3();", + "const tempV2$1 = new THREE.Vector3();", + "new THREE.Vector3();", + "const PREWARM_FPS$1 = 60;", + "const DEFAULT_GEOMETRY$1 = new THREE.PlaneGeometry(1, 1, 1, 1);", + "class ParticleSystem {", + " set time(time) {", + " this.emissionState.time = time;", + " }", + " get time() {", + " return this.emissionState.time;", + " }", + " get layers() {", + " return this.rendererSettings.layers;", + " }", + " get texture() {", + " return this.rendererSettings.material.map;", + " }", + " set texture(texture) {", + " this.rendererSettings.material.map = texture;", + " this.neededToUpdateRender = true;", + " }", + " get material() {", + " return this.rendererSettings.material;", + " }", + " set material(material) {", + " this.rendererSettings.material = material;", + " this.neededToUpdateRender = true;", + " }", + " get uTileCount() {", + " return this.rendererSettings.uTileCount;", + " }", + " set uTileCount(u) {", + " this.rendererSettings.uTileCount = u;", + " this.neededToUpdateRender = true;", + " }", + " get vTileCount() {", + " return this.rendererSettings.vTileCount;", + " }", + " set vTileCount(v) {", + " this.rendererSettings.vTileCount = v;", + " this.neededToUpdateRender = true;", + " }", + " get instancingGeometry() {", + " return this.rendererSettings.instancingGeometry;", + " }", + " set instancingGeometry(geometry) {", + " this.restart();", + " this.particles.length = 0;", + " this.rendererSettings.instancingGeometry = geometry;", + " this.neededToUpdateRender = true;", + " }", + " get renderMode() {", + " return this.rendererSettings.renderMode;", + " }", + " set renderMode(renderMode) {", + " if ((this.rendererSettings.renderMode != RenderMode.Trail && renderMode === RenderMode.Trail) ||", + " (this.rendererSettings.renderMode == RenderMode.Trail && renderMode !== RenderMode.Trail)) {", + " this.restart();", + " this.particles.length = 0;", + " }", + " if (this.rendererSettings.renderMode !== renderMode) {", + " switch (renderMode) {", + " case RenderMode.Trail:", + " this.rendererEmitterSettings = {", + " startLength: new ConstantValue(30),", + " followLocalOrigin: false,", + " };", + " break;", + " case RenderMode.Mesh:", + " this.rendererEmitterSettings = {", + " geometry: new THREE.PlaneGeometry(1, 1),", + " };", + " this.startRotation = new AxisAngleGenerator(new THREE.Vector3(0, 1, 0), new ConstantValue(0));", + " break;", + " case RenderMode.StretchedBillBoard:", + " this.rendererEmitterSettings = { speedFactor: 0, lengthFactor: 2 };", + " if (this.rendererSettings.renderMode === RenderMode.Mesh) {", + " this.startRotation = new ConstantValue(0);", + " }", + " break;", + " case RenderMode.BillBoard:", + " case RenderMode.VerticalBillBoard:", + " case RenderMode.HorizontalBillBoard:", + " this.rendererEmitterSettings = {};", + " if (this.rendererSettings.renderMode === RenderMode.Mesh) {", + " this.startRotation = new ConstantValue(0);", + " }", + " break;", + " }", + " }", + " this.rendererSettings.renderMode = renderMode;", + " this.neededToUpdateRender = true;", + " }", + " get renderOrder() {", + " return this.rendererSettings.renderOrder;", + " }", + " set renderOrder(renderOrder) {", + " this.rendererSettings.renderOrder = renderOrder;", + " this.neededToUpdateRender = true;", + " }", + " get blending() {", + " return this.rendererSettings.material.blending;", + " }", + " set blending(blending) {", + " this.rendererSettings.material.blending = blending;", + " this.neededToUpdateRender = true;", + " }", + " constructor(parameters) {", + " var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m, _o, _p, _q, _r, _s, _t, _u, _v, _w, _x;", + " this.temp = new THREE.Vector3();", + " this.travelDistance = 0;", + " this.normalMatrix = new THREE.Matrix3();", + " this.firstTimeUpdate = true;", + " this.autoDestroy = parameters.autoDestroy === undefined ? false : parameters.autoDestroy;", + " this.duration = (_a = parameters.duration) !== null && _a !== void 0 ? _a : 1;", + " this.looping = parameters.looping === undefined ? true : parameters.looping;", + " this.prewarm = parameters.prewarm === undefined ? false : parameters.prewarm;", + " this.startLife = (_b = parameters.startLife) !== null && _b !== void 0 ? _b : new ConstantValue(5);", + " this.startSpeed = (_c = parameters.startSpeed) !== null && _c !== void 0 ? _c : new ConstantValue(0);", + " this.startRotation = (_d = parameters.startRotation) !== null && _d !== void 0 ? _d : new ConstantValue(0);", + " this.startSize = (_e = parameters.startSize) !== null && _e !== void 0 ? _e : new ConstantValue(1);", + " this.startColor = (_f = parameters.startColor) !== null && _f !== void 0 ? _f : new ConstantColor(new THREE.Vector4(1, 1, 1, 1));", + " this.emissionOverTime = (_g = parameters.emissionOverTime) !== null && _g !== void 0 ? _g : new ConstantValue(10);", + " this.emissionOverDistance = (_h = parameters.emissionOverDistance) !== null && _h !== void 0 ? _h : new ConstantValue(0);", + " this.emissionBursts = (_j = parameters.emissionBursts) !== null && _j !== void 0 ? _j : [];", + " this.onlyUsedByOther = (_k = parameters.onlyUsedByOther) !== null && _k !== void 0 ? _k : false;", + " this.emitterShape = (_l = parameters.shape) !== null && _l !== void 0 ? _l : new SphereEmitter();", + " this.behaviors = (_m = parameters.behaviors) !== null && _m !== void 0 ? _m : new Array();", + " this.worldSpace = (_o = parameters.worldSpace) !== null && _o !== void 0 ? _o : false;", + " this.rendererEmitterSettings = (_p = parameters.rendererEmitterSettings) !== null && _p !== void 0 ? _p : {};", + " if (parameters.renderMode === RenderMode.StretchedBillBoard) {", + " const stretchedBillboardSettings = this.rendererEmitterSettings;", + " if (parameters.speedFactor !== undefined) {", + " stretchedBillboardSettings.speedFactor = parameters.speedFactor;", + " }", + " stretchedBillboardSettings.speedFactor = (_q = stretchedBillboardSettings.speedFactor) !== null && _q !== void 0 ? _q : 0;", + " stretchedBillboardSettings.lengthFactor = (_r = stretchedBillboardSettings.lengthFactor) !== null && _r !== void 0 ? _r : 0;", + " }", + " this.rendererSettings = {", + " instancingGeometry: (_s = parameters.instancingGeometry) !== null && _s !== void 0 ? _s : DEFAULT_GEOMETRY$1,", + " renderMode: (_t = parameters.renderMode) !== null && _t !== void 0 ? _t : RenderMode.BillBoard,", + " renderOrder: (_u = parameters.renderOrder) !== null && _u !== void 0 ? _u : 0,", + " material: parameters.material,", + " uTileCount: (_v = parameters.uTileCount) !== null && _v !== void 0 ? _v : 1,", + " vTileCount: (_w = parameters.vTileCount) !== null && _w !== void 0 ? _w : 1,", + " layers: (_x = parameters.layers) !== null && _x !== void 0 ? _x : new THREE.Layers(),", + " };", + " this.neededToUpdateRender = true;", + " this.particles = new Array();", + " this.startTileIndex = parameters.startTileIndex || new ConstantValue(0);", + " this.emitter = new ParticleEmitter(this);", + " this.paused = false;", + " this.particleNum = 0;", + " this.emissionState = {", + " burstIndex: 0,", + " burstWaveIndex: 0,", + " time: 0,", + " waitEmiting: 0,", + " travelDistance: 0,", + " };", + " this.emitEnded = false;", + " this.markForDestroy = false;", + " this.prewarmed = false;", + " }", + " pause() {", + " this.paused = true;", + " }", + " play() {", + " this.paused = false;", + " }", + " spawn(count, emissionState, matrix) {", + " tempQ$1.setFromRotationMatrix(matrix);", + " const translation = tempV$1;", + " const quaternion = tempQ$1;", + " const scale = tempV2$1;", + " matrix.decompose(translation, quaternion, scale);", + " for (let i = 0; i < count; i++) {", + " this.particleNum++;", + " while (this.particles.length < this.particleNum) {", + " if (this.rendererSettings.renderMode === RenderMode.Trail) {", + " this.particles.push(new TrailParticle());", + " }", + " else {", + " this.particles.push(new SpriteParticle());", + " }", + " }", + " const particle = this.particles[this.particleNum - 1];", + " particle.speedModifier = 1;", + " this.startColor.genColor(particle.startColor, this.emissionState.time, {});", + " particle.color.copy(particle.startColor);", + " particle.startSpeed = this.startSpeed.genValue(emissionState.time / this.duration);", + " particle.life = this.startLife.genValue(emissionState.time / this.duration);", + " particle.age = 0;", + " particle.startSize = this.startSize.genValue(emissionState.time / this.duration);", + " particle.uvTile = Math.floor(this.startTileIndex.genValue() + 0.001);", + " particle.size = particle.startSize;", + " if (this.rendererSettings.renderMode === RenderMode.Mesh ||", + " this.rendererSettings.renderMode === RenderMode.BillBoard ||", + " this.rendererSettings.renderMode === RenderMode.VerticalBillBoard ||", + " this.rendererSettings.renderMode === RenderMode.HorizontalBillBoard ||", + " this.rendererSettings.renderMode === RenderMode.StretchedBillBoard) {", + " const sprite = particle;", + " if (this.rendererSettings.renderMode === RenderMode.Mesh) {", + " if (!(sprite.rotation instanceof THREE.Quaternion)) {", + " sprite.rotation = new THREE.Quaternion();", + " }", + " if (this.startRotation.type === 'rotation') {", + " this.startRotation.genValue(sprite.rotation, emissionState.time / this.duration);", + " }", + " else {", + " sprite.rotation.setFromAxisAngle(UP, this.startRotation.genValue((emissionState.time / this.duration)));", + " }", + " }", + " else {", + " if (this.startRotation.type === 'rotation') {", + " sprite.rotation = 0;", + " }", + " else {", + " sprite.rotation = this.startRotation.genValue(emissionState.time / this.duration);", + " }", + " }", + " }", + " else if (this.rendererSettings.renderMode === RenderMode.Trail) {", + " const trail = particle;", + " trail.length = this.rendererEmitterSettings.startLength.genValue(emissionState.time / this.duration);", + " }", + " this.emitterShape.initialize(particle);", + " if (this.rendererSettings.renderMode === RenderMode.Trail &&", + " this.rendererEmitterSettings.followLocalOrigin) {", + " const trail = particle;", + " trail.localPosition = new THREE.Vector3().copy(trail.position);", + " }", + " if (this.worldSpace) {", + " particle.position.applyMatrix4(matrix);", + " particle.startSize =", + " (particle.startSize * (Math.abs(scale.x) + Math.abs(scale.y) + Math.abs(scale.z))) / 3;", + " particle.size = particle.startSize;", + " particle.velocity.multiply(scale).applyMatrix3(this.normalMatrix);", + " if (particle.rotation && particle.rotation instanceof THREE.Quaternion) {", + " particle.rotation.multiplyQuaternions(tempQ$1, particle.rotation);", + " }", + " }", + " else {", + " if (this.onlyUsedByOther) {", + " particle.parentMatrix = matrix;", + " }", + " }", + " for (let j = 0; j < this.behaviors.length; j++) {", + " this.behaviors[j].initialize(particle);", + " }", + " }", + " }", + " endEmit() {", + " this.emitEnded = true;", + " if (this.autoDestroy) {", + " this.markForDestroy = true;", + " }", + " }", + " dispose() {", + " if (this._renderer)", + " this._renderer.deleteSystem(this);", + " this.emitter.dispose();", + " if (this.emitter.parent)", + " this.emitter.parent.remove(this.emitter);", + " }", + " restart() {", + " this.paused = false;", + " this.particleNum = 0;", + " this.emissionState.burstIndex = 0;", + " this.emissionState.burstWaveIndex = 0;", + " this.emissionState.time = 0;", + " this.emissionState.waitEmiting = 0;", + " this.behaviors.forEach((behavior) => {", + " behavior.reset();", + " });", + " this.emitEnded = false;", + " this.markForDestroy = false;", + " this.prewarmed = false;", + " }", + " update(delta) {", + " if (this.paused)", + " return;", + " let currentParent = this.emitter;", + " while (currentParent.parent) {", + " currentParent = currentParent.parent;", + " }", + " if (currentParent.type !== 'Scene') {", + " this.dispose();", + " return;", + " }", + " if (this.firstTimeUpdate) {", + " this.firstTimeUpdate = false;", + " this.emitter.updateWorldMatrix(true, false);", + " }", + " if (this.emitEnded && this.particleNum === 0) {", + " if (this.markForDestroy && this.emitter.parent)", + " this.dispose();", + " return;", + " }", + " if (this.looping && this.prewarm && !this.prewarmed) {", + " this.prewarmed = true;", + " for (let i = 0; i < this.duration * PREWARM_FPS$1; i++) {", + " this.update(1.0 / PREWARM_FPS$1);", + " }", + " }", + " if (delta > 0.1) {", + " delta = 0.1;", + " }", + " if (this.neededToUpdateRender) {", + " if (this._renderer)", + " this._renderer.updateSystem(this);", + " this.neededToUpdateRender = false;", + " }", + " if (!this.onlyUsedByOther) {", + " this.emit(delta, this.emissionState, this.emitter.matrixWorld);", + " }", + " this.emitterShape.update(this, delta);", + " for (let j = 0; j < this.behaviors.length; j++) {", + " for (let i = 0; i < this.particleNum; i++) {", + " if (!this.particles[i].died) {", + " this.behaviors[j].update(this.particles[i], delta);", + " }", + " }", + " this.behaviors[j].frameUpdate(delta);", + " }", + " for (let i = 0; i < this.particleNum; i++) {", + " if (this.rendererEmitterSettings.followLocalOrigin &&", + " this.particles[i].localPosition) {", + " this.particles[i].position.copy(this.particles[i].localPosition);", + " if (this.particles[i].parentMatrix) {", + " this.particles[i].position.applyMatrix4(this.particles[i].parentMatrix);", + " }", + " else {", + " this.particles[i].position.applyMatrix4(this.emitter.matrixWorld);", + " }", + " }", + " else {", + " this.particles[i].position.addScaledVector(this.particles[i].velocity, delta * this.particles[i].speedModifier);", + " }", + " this.particles[i].age += delta;", + " }", + " if (this.rendererSettings.renderMode === RenderMode.Trail) {", + " for (let i = 0; i < this.particleNum; i++) {", + " const particle = this.particles[i];", + " particle.update();", + " }", + " }", + " for (let i = 0; i < this.particleNum; i++) {", + " const particle = this.particles[i];", + " if (particle.died && (!(particle instanceof TrailParticle) || particle.previous.length === 0)) {", + " this.particles[i] = this.particles[this.particleNum - 1];", + " this.particles[this.particleNum - 1] = particle;", + " this.particleNum--;", + " i--;", + " }", + " }", + " }", + " emit(delta, emissionState, emitterMatrix) {", + " if (emissionState.time > this.duration) {", + " if (this.looping) {", + " emissionState.time -= this.duration;", + " emissionState.burstIndex = 0;", + " this.behaviors.forEach((behavior) => {", + " behavior.reset();", + " });", + " }", + " else {", + " if (!this.emitEnded && !this.onlyUsedByOther) {", + " this.endEmit();", + " }", + " }", + " }", + " this.normalMatrix.getNormalMatrix(emitterMatrix);", + " const totalSpawn = Math.ceil(emissionState.waitEmiting);", + " this.spawn(totalSpawn, emissionState, emitterMatrix);", + " emissionState.waitEmiting -= totalSpawn;", + " while (emissionState.burstIndex < this.emissionBursts.length &&", + " this.emissionBursts[emissionState.burstIndex].time <= emissionState.time) {", + " if (Math.random() < this.emissionBursts[emissionState.burstIndex].probability) {", + " const count = this.emissionBursts[emissionState.burstIndex].count.genValue(this.time);", + " this.spawn(count, emissionState, emitterMatrix);", + " }", + " emissionState.burstIndex++;", + " }", + " if (!this.emitEnded) {", + " emissionState.waitEmiting += delta * this.emissionOverTime.genValue(emissionState.time / this.duration);", + " if (emissionState.previousWorldPos != undefined) {", + " this.temp.set(emitterMatrix.elements[12], emitterMatrix.elements[13], emitterMatrix.elements[14]);", + " emissionState.travelDistance += emissionState.previousWorldPos.distanceTo(this.temp);", + " const emitPerMeter = this.emissionOverDistance.genValue(emissionState.time / this.duration);", + " if (emissionState.travelDistance * emitPerMeter > 0) {", + " const count = Math.floor(emissionState.travelDistance * emitPerMeter);", + " emissionState.travelDistance -= count / emitPerMeter;", + " emissionState.waitEmiting += count;", + " }", + " }", + " }", + " if (emissionState.previousWorldPos === undefined)", + " emissionState.previousWorldPos = new THREE.Vector3();", + " emissionState.previousWorldPos.set(emitterMatrix.elements[12], emitterMatrix.elements[13], emitterMatrix.elements[14]);", + " emissionState.time += delta;", + " }", + " toJSON(meta, options = {}) {", + " const isRootObject = meta === undefined || typeof meta === 'string';", + " if (isRootObject) {", + " meta = {", + " geometries: {},", + " materials: {},", + " textures: {},", + " images: {},", + " shapes: {},", + " skeletons: {},", + " animations: {},", + " nodes: {},", + " };", + " }", + " meta.materials[this.rendererSettings.material.uuid] = this.rendererSettings.material.toJSON(meta);", + " if (options.useUrlForImage) {", + " if (this.texture.source !== undefined) {", + " const image = this.texture.source;", + " meta.images[image.uuid] = {", + " uuid: image.uuid,", + " url: this.texture.image.url,", + " };", + " }", + " }", + " let rendererSettingsJSON;", + " if (this.renderMode === RenderMode.Trail) {", + " rendererSettingsJSON = {", + " startLength: this.rendererEmitterSettings.startLength.toJSON(),", + " followLocalOrigin: this.rendererEmitterSettings.followLocalOrigin,", + " };", + " }", + " else if (this.renderMode === RenderMode.Mesh) {", + " rendererSettingsJSON = {};", + " }", + " else if (this.renderMode === RenderMode.StretchedBillBoard) {", + " rendererSettingsJSON = {", + " speedFactor: this.rendererEmitterSettings.speedFactor,", + " lengthFactor: this.rendererEmitterSettings.lengthFactor,", + " };", + " }", + " else {", + " rendererSettingsJSON = {};", + " }", + " const geometry = this.rendererSettings.instancingGeometry;", + " if (meta.geometries && !meta.geometries[geometry.uuid]) {", + " meta.geometries[geometry.uuid] = geometry.toJSON();", + " }", + " return {", + " version: '2.0',", + " autoDestroy: this.autoDestroy,", + " looping: this.looping,", + " prewarm: this.prewarm,", + " duration: this.duration,", + " shape: this.emitterShape.toJSON(),", + " startLife: this.startLife.toJSON(),", + " startSpeed: this.startSpeed.toJSON(),", + " startRotation: this.startRotation.toJSON(),", + " startSize: this.startSize.toJSON(),", + " startColor: this.startColor.toJSON(),", + " emissionOverTime: this.emissionOverTime.toJSON(),", + " emissionOverDistance: this.emissionOverDistance.toJSON(),", + " emissionBursts: this.emissionBursts.map((burst) => ({", + " time: burst.time,", + " count: burst.count.toJSON(),", + " probability: burst.probability,", + " interval: burst.interval,", + " cycle: burst.cycle,", + " })),", + " onlyUsedByOther: this.onlyUsedByOther,", + " instancingGeometry: this.rendererSettings.instancingGeometry.uuid,", + " renderOrder: this.renderOrder,", + " renderMode: this.renderMode,", + " rendererEmitterSettings: rendererSettingsJSON,", + " material: this.rendererSettings.material.uuid,", + " layers: this.layers.mask,", + " startTileIndex: this.startTileIndex.toJSON(),", + " uTileCount: this.uTileCount,", + " vTileCount: this.vTileCount,", + " behaviors: this.behaviors.map((behavior) => behavior.toJSON()),", + " worldSpace: this.worldSpace,", + " };", + " }", + " static fromJSON(json, meta, dependencies) {", + " var _a, _b;", + " const shape = EmitterFromJSON(json.shape, meta);", + " let rendererEmitterSettings;", + " if (json.renderMode === RenderMode.Trail) {", + " let trailSettings = json.rendererEmitterSettings;", + " rendererEmitterSettings = {", + " startLength: trailSettings.startLength != undefined", + " ? ValueGeneratorFromJSON(trailSettings.startLength)", + " : new ConstantValue(30),", + " followLocalOrigin: trailSettings.followLocalOrigin,", + " };", + " }", + " else if (json.renderMode === RenderMode.Mesh) {", + " rendererEmitterSettings = {};", + " }", + " else if (json.renderMode === RenderMode.StretchedBillBoard) {", + " rendererEmitterSettings = json.rendererEmitterSettings;", + " if (json.speedFactor != undefined) {", + " rendererEmitterSettings.speedFactor = json.speedFactor;", + " }", + " }", + " else {", + " rendererEmitterSettings = {};", + " }", + " const layers = new THREE.Layers();", + " if (json.layers) {", + " layers.mask = json.layers;", + " }", + " const ps = new ParticleSystem({", + " autoDestroy: json.autoDestroy,", + " looping: json.looping,", + " prewarm: json.prewarm,", + " duration: json.duration,", + " shape: shape,", + " startLife: ValueGeneratorFromJSON(json.startLife),", + " startSpeed: ValueGeneratorFromJSON(json.startSpeed),", + " startRotation: GeneratorFromJSON(json.startRotation),", + " startSize: ValueGeneratorFromJSON(json.startSize),", + " startColor: ColorGeneratorFromJSON(json.startColor),", + " emissionOverTime: ValueGeneratorFromJSON(json.emissionOverTime),", + " emissionOverDistance: ValueGeneratorFromJSON(json.emissionOverDistance),", + " emissionBursts: (_a = json.emissionBursts) === null || _a === void 0 ? void 0 : _a.map((burst) => ({", + " time: burst.time,", + " count: typeof burst.count === 'number'", + " ? new ConstantValue(burst.count)", + " : ValueGeneratorFromJSON(burst.count),", + " probability: burst.probability,", + " interval: burst.interval,", + " cycle: burst.cycle,", + " })),", + " onlyUsedByOther: json.onlyUsedByOther,", + " instancingGeometry: meta.geometries[json.instancingGeometry],", + " renderMode: json.renderMode,", + " rendererEmitterSettings: rendererEmitterSettings,", + " renderOrder: json.renderOrder,", + " layers: layers,", + " material: json.material", + " ? meta.materials[json.material]", + " : json.texture", + " ? new THREE.MeshBasicMaterial({", + " map: meta.textures[json.texture],", + " transparent: (_b = json.transparent) !== null && _b !== void 0 ? _b : true,", + " blending: json.blending,", + " side: THREE.DoubleSide,", + " })", + " : new THREE.MeshBasicMaterial({", + " color: 0xffffff,", + " transparent: true,", + " blending: THREE.AdditiveBlending,", + " side: THREE.DoubleSide,", + " }),", + " startTileIndex: typeof json.startTileIndex === 'number'", + " ? new ConstantValue(json.startTileIndex)", + " : ValueGeneratorFromJSON(json.startTileIndex),", + " uTileCount: json.uTileCount,", + " vTileCount: json.vTileCount,", + " behaviors: [],", + " worldSpace: json.worldSpace,", + " });", + " ps.behaviors = json.behaviors.map((behaviorJson) => {", + " const behavior = BehaviorFromJSON(behaviorJson, ps);", + " if (behavior.type === 'EmitSubParticleSystem') {", + " dependencies[behaviorJson.subParticleSystem] = behavior;", + " }", + " return behavior;", + " });", + " return ps;", + " }", + " addBehavior(behavior) {", + " this.behaviors.push(behavior);", + " }", + " getRendererSettings() {", + " return this.rendererSettings;", + " }", + " clone() {", + " const newEmissionBursts = [];", + " for (const emissionBurst of this.emissionBursts) {", + " const newEmissionBurst = {};", + " Object.assign(newEmissionBurst, emissionBurst);", + " newEmissionBursts.push(newEmissionBurst);", + " }", + " const newBehaviors = [];", + " for (const behavior of this.behaviors) {", + " newBehaviors.push(behavior.clone());", + " }", + " let rendererEmitterSettings;", + " if (this.renderMode === RenderMode.Trail) {", + " rendererEmitterSettings = {", + " startLength: this.rendererEmitterSettings.startLength.clone(),", + " followLocalOrigin: this.rendererEmitterSettings.followLocalOrigin,", + " };", + " }", + " else {", + " rendererEmitterSettings = {};", + " }", + " const layers = new THREE.Layers();", + " layers.mask = this.layers.mask;", + " return new ParticleSystem({", + " autoDestroy: this.autoDestroy,", + " looping: this.looping,", + " duration: this.duration,", + " shape: this.emitterShape.clone(),", + " startLife: this.startLife.clone(),", + " startSpeed: this.startSpeed.clone(),", + " startRotation: this.startRotation.clone(),", + " startSize: this.startSize.clone(),", + " startColor: this.startColor.clone(),", + " emissionOverTime: this.emissionOverTime.clone(),", + " emissionOverDistance: this.emissionOverDistance.clone(),", + " emissionBursts: newEmissionBursts,", + " onlyUsedByOther: this.onlyUsedByOther,", + " instancingGeometry: this.rendererSettings.instancingGeometry,", + " renderMode: this.renderMode,", + " renderOrder: this.renderOrder,", + " rendererEmitterSettings: rendererEmitterSettings,", + " material: this.rendererSettings.material,", + " startTileIndex: this.startTileIndex,", + " uTileCount: this.uTileCount,", + " vTileCount: this.vTileCount,", + " behaviors: newBehaviors,", + " worldSpace: this.worldSpace,", + " layers: layers,", + " });", + " }", + "}", + "", + "var particle_frag = `", + "", + "#include ", + "#include ", + "#include ", + "#include ", + "#include ", + "#include ", + "#include ", + "", + "void main() {", + "", + " #include ", + " ", + " vec3 outgoingLight = vec3( 0.0 );", + " vec4 diffuseColor = vColor;", + " ", + " #include ", + " ", + " #ifdef USE_MAP", + " diffuseColor *= texture2D( map, vMapUv);", + " #endif", + " ", + " #include ", + "", + " outgoingLight = diffuseColor.rgb;", + " ", + " #ifdef USE_COLOR_AS_ALPHA", + " gl_FragColor = vec4( outgoingLight, diffuseColor.r );", + " #else", + " gl_FragColor = vec4( outgoingLight, diffuseColor.a );", + " #endif", + " ", + " ", + " #include ", + "", + "}", + "`;", + "", + "var particle_physics_frag = `", + "#define STANDARD", + "#ifdef PHYSICAL", + "#define IOR", + "#define SPECULAR", + "#endif", + "uniform vec3 diffuse;", + "uniform vec3 emissive;", + "uniform float roughness;", + "uniform float metalness;", + "uniform float opacity;", + "#ifdef IOR", + "uniform float ior;", + "#endif", + "#ifdef SPECULAR", + "uniform float specularIntensity;", + "uniform vec3 specularColor;", + "#ifdef USE_SPECULARINTENSITYMAP", + "uniform sampler2D specularIntensityMap;", + "#endif", + "#ifdef USE_SPECULARCOLORMAP", + "uniform sampler2D specularColorMap;", + "#endif", + "#endif", + "#ifdef USE_CLEARCOAT", + "uniform float clearcoat;", + "uniform float clearcoatRoughness;", + "#endif", + "#ifdef USE_IRIDESCENCE", + "uniform float iridescence;", + "uniform float iridescenceIOR;", + "uniform float iridescenceThicknessMinimum;", + "uniform float iridescenceThicknessMaximum;", + "#endif", + "#ifdef USE_SHEEN", + "uniform vec3 sheenColor;", + "uniform float sheenRoughness;", + "#ifdef USE_SHEENCOLORMAP", + "uniform sampler2D sheenColorMap;", + "#endif", + "#ifdef USE_SHEENROUGHNESSMAP", + "uniform sampler2D sheenRoughnessMap;", + "#endif", + "#endif", + "", + "varying vec3 vViewPosition;", + "#include ", + "#include ", + "#include ", + "#include ", + "#include ", + "#include ", + "#include ", + "#include ", + "#include ", + "#include ", + "#include ", + "#include ", + "#include ", + "#include ", + "#include ", + "#include ", + "#include ", + "#include ", + "#include ", + "#include ", + "#include ", + "#include ", + "#include ", + "#include ", + "#include ", + "#include ", + "#include ", + "#include ", + "#include ", + "#include ", + "", + "void main() {", + " #include ", + " vec4 diffuseColor = vec4( diffuse, opacity );", + " ReflectedLight reflectedLight = ReflectedLight( vec3( 0.0 ), vec3( 0.0 ), vec3( 0.0 ), vec3( 0.0 ) );", + " vec3 totalEmissiveRadiance = emissive;", + " #include ", + " #include ", + " #include ", + " #include ", + " #include ", + " #include ", + " #include ", + " #include ", + " #include ", + " #include ", + " #include ", + " #include ", + " // accumulation", + " #include ", + " #include ", + " #include ", + " #include ", + " // modulation", + " #include ", + " vec3 totalDiffuse = reflectedLight.directDiffuse + reflectedLight.indirectDiffuse;", + " vec3 totalSpecular = reflectedLight.directSpecular + reflectedLight.indirectSpecular;", + " #include ", + " vec3 outgoingLight = totalDiffuse + totalSpecular + totalEmissiveRadiance;", + " #ifdef USE_SHEEN", + " // Sheen energy compensation approximation calculation can be found at the end of", + " // https://drive.google.com/file/d/1T0D1VSyR4AllqIJTQAraEIzjlb5h4FKH/view?usp=sharing", + " float sheenEnergyComp = 1.0 - 0.157 * max3( material.sheenColor );", + " outgoingLight = outgoingLight * sheenEnergyComp + sheenSpecular;", + " #endif", + " #ifdef USE_CLEARCOAT", + " float dotNVcc = saturate( dot( geometry.clearcoatNormal, geometry.viewDir ) );", + " vec3 Fcc = F_Schlick( material.clearcoatF0, material.clearcoatF90, dotNVcc );", + " outgoingLight = outgoingLight * ( 1.0 - material.clearcoat * Fcc ) + clearcoatSpecular * material.clearcoat;", + " #endif", + " #include ", + " #include ", + " #include ", + " #include ", + " #include ", + " #include ", + "}`;", + "", + "var uv_vertex_tile = `", + "", + " #ifdef UV_TILE", + " float col = mod(uvTile, tileCount.x);", + " float row = (tileCount.y - floor(uvTile / tileCount.x) - 1.0);", + " ", + " mat3 tileTransform = mat3(", + " 1.0 / tileCount.x, 0.0, 0.0,", + " 0.0, 1.0 / tileCount.y, 0.0, ", + " col / tileCount.x, row / tileCount.y, 1.0);", + " #else", + " mat3 tileTransform = mat3(1.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 1.0);", + " #endif", + "", + "#if defined( USE_UV ) || defined( USE_ANISOTROPY )", + "", + "vUv = (tileTransform *vec3( uv, 1 )).xy;", + "", + "#endif", + "#ifdef USE_MAP", + "", + "vMapUv = ( tileTransform * (mapTransform * vec3( MAP_UV, 1 ) )).xy;", + "", + "#endif", + "#ifdef USE_ALPHAMAP", + "", + "vAlphaMapUv = ( tileTransform * (alphaMapTransform * vec3( ALPHAMAP_UV, 1 ) )).xy;", + "", + "#endif", + "#ifdef USE_LIGHTMAP", + "", + "vLightMapUv = ( tileTransform * (lightMapTransform * vec3( LIGHTMAP_UV, 1 ) )).xy;", + "", + "#endif", + "#ifdef USE_AOMAP", + "", + "vAoMapUv = ( tileTransform * (aoMapTransform * vec3( AOMAP_UV, 1 ) )).xy;", + "", + "#endif", + "#ifdef USE_BUMPMAP", + "", + "vBumpMapUv = ( tileTransform * (bumpMapTransform * vec3( BUMPMAP_UV, 1 ) )).xy;", + "", + "#endif", + "#ifdef USE_NORMALMAP", + "", + "vNormalMapUv = ( tileTransform * (normalMapTransform * vec3( NORMALMAP_UV, 1 ) )).xy;", + "", + "#endif", + "#ifdef USE_DISPLACEMENTMAP", + "", + "vDisplacementMapUv = ( tileTransform * (displacementMapTransform * vec3( DISPLACEMENTMAP_UV, 1 ) )).xy;", + "", + "#endif", + "#ifdef USE_EMISSIVEMAP", + "", + "vEmissiveMapUv = ( tileTransform * (emissiveMapTransform * vec3( EMISSIVEMAP_UV, 1 ) )).xy;", + "", + "#endif", + "#ifdef USE_METALNESSMAP", + "", + "vMetalnessMapUv = ( tileTransform * (metalnessMapTransform * vec3( METALNESSMAP_UV, 1 ) )).xy;", + "", + "#endif", + "#ifdef USE_ROUGHNESSMAP", + "", + "vRoughnessMapUv = ( tileTransform * (roughnessMapTransform * vec3( ROUGHNESSMAP_UV, 1 ) )).xy;", + "", + "#endif", + "#ifdef USE_ANISOTROPYMAP", + "", + "vAnisotropyMapUv = ( tileTransform * (anisotropyMapTransform * vec3( ANISOTROPYMAP_UV, 1 ) )).xy;", + "", + "#endif", + "#ifdef USE_CLEARCOATMAP", + "", + "vClearcoatMapUv = ( tileTransform * (clearcoatMapTransform * vec3( CLEARCOATMAP_UV, 1 ) )).xy;", + "", + "#endif", + "#ifdef USE_CLEARCOAT_NORMALMAP", + "", + "vClearcoatNormalMapUv = ( tileTransform * (clearcoatNormalMapTransform * vec3( CLEARCOAT_NORMALMAP_UV, 1 ) )).xy;", + "", + "#endif", + "#ifdef USE_CLEARCOAT_ROUGHNESSMAP", + "", + "vClearcoatRoughnessMapUv = ( tileTransform * (clearcoatRoughnessMapTransform * vec3( CLEARCOAT_ROUGHNESSMAP_UV, 1 ) )).xy;", + "", + "#endif", + "#ifdef USE_IRIDESCENCEMAP", + "", + "vIridescenceMapUv = ( tileTransform * (iridescenceMapTransform * vec3( IRIDESCENCEMAP_UV, 1 ) )).xy;", + "", + "#endif", + "#ifdef USE_IRIDESCENCE_THICKNESSMAP", + "", + "vIridescenceThicknessMapUv = ( tileTransform * (iridescenceThicknessMapTransform * vec3( IRIDESCENCE_THICKNESSMAP_UV, 1 ) )).xy;", + "", + "#endif", + "#ifdef USE_SHEEN_COLORMAP", + "", + "vSheenColorMapUv = ( tileTransform * (sheenColorMapTransform * vec3( SHEEN_COLORMAP_UV, 1 ) )).xy;", + "", + "#endif", + "#ifdef USE_SHEEN_ROUGHNESSMAP", + "", + "vSheenRoughnessMapUv = ( tileTransform * (sheenRoughnessMapTransform * vec3( SHEEN_ROUGHNESSMAP_UV, 1 ) )).xy;", + "", + "#endif", + "#ifdef USE_SPECULARMAP", + "", + "vSpecularMapUv = ( tileTransform * (specularMapTransform * vec3( SPECULARMAP_UV, 1 ) )).xy;", + "", + "#endif", + "#ifdef USE_SPECULAR_COLORMAP", + "", + "vSpecularColorMapUv = ( tileTransform * (specularColorMapTransform * vec3( SPECULAR_COLORMAP_UV, 1 ) )).xy;", + "", + "#endif", + "#ifdef USE_SPECULAR_INTENSITYMAP", + "", + "vSpecularIntensityMapUv = ( tileTransform * (specularIntensityMapTransform * vec3( SPECULAR_INTENSITYMAP_UV, 1 ) )).xy;", + "", + "#endif", + "#ifdef USE_TRANSMISSIONMAP", + "", + "vTransmissionMapUv = ( tileTransform * transmissionMapTransform * vec3( TRANSMISSIONMAP_UV, 1 ) )).xy;", + "", + "#endif", + "#ifdef USE_THICKNESSMAP", + "", + "vThicknessMapUv = ( tileTransform * thicknessMapTransform * vec3( THICKNESSMAP_UV, 1 ) )).xy;", + "", + "#endif", + "`;", + "", + "var particle_vert = `", + "#include ", + "#include ", + "#include ", + "#include ", + "#include ", + "", + "attribute vec3 offset;", + "attribute float rotation;", + "attribute float size;", + "attribute float uvTile;", + "", + "#ifdef UV_TILE", + "uniform vec2 tileCount;", + "#endif", + "", + "void main() {", + "", + " ${uv_vertex_tile}", + "\t", + " vec2 alignedPosition = ( position.xy ) * size;", + " ", + " vec2 rotatedPosition;", + " rotatedPosition.x = cos( rotation ) * alignedPosition.x - sin( rotation ) * alignedPosition.y;", + " rotatedPosition.y = sin( rotation ) * alignedPosition.x + cos( rotation ) * alignedPosition.y;", + "#ifdef HORIZONTAL", + " vec4 mvPosition = modelMatrix * vec4( offset, 1.0 );", + " mvPosition.x += rotatedPosition.x;", + " mvPosition.z -= rotatedPosition.y;", + " mvPosition = viewMatrix * mvPosition;", + "#elif defined(VERTICAL)", + " vec4 mvPosition = modelMatrix * vec4( offset, 1.0 );", + " mvPosition.y += rotatedPosition.y;", + " mvPosition = viewMatrix * mvPosition;", + " mvPosition.x += rotatedPosition.x;", + "#else", + " vec4 mvPosition = modelViewMatrix * vec4( offset, 1.0 );", + " mvPosition.xy += rotatedPosition;", + "#endif", + "", + "\tvColor = color;", + "", + "\tgl_Position = projectionMatrix * mvPosition;", + "", + "\t#include ", + "\t#include ", + "", + "}", + "`;", + "", + "var local_particle_vert = `", + "#include ", + "#include ", + "#include ", + "#include ", + "#include ", + "", + "attribute vec3 offset;", + "attribute vec4 rotation;", + "attribute float size;", + "// attribute vec4 color;", + "attribute float uvTile;", + "", + "#ifdef UV_TILE", + "uniform vec2 tileCount;", + "#endif", + "", + "void main() {", + "", + " ${uv_vertex_tile}", + " ", + " float x2 = rotation.x + rotation.x, y2 = rotation.y + rotation.y, z2 = rotation.z + rotation.z;", + " float xx = rotation.x * x2, xy = rotation.x * y2, xz = rotation.x * z2;", + " float yy = rotation.y * y2, yz = rotation.y * z2, zz = rotation.z * z2;", + " float wx = rotation.w * x2, wy = rotation.w * y2, wz = rotation.w * z2;", + " float sx = size, sy = size, sz = size;", + " ", + " mat4 matrix = mat4(( 1.0 - ( yy + zz ) ) * sx, ( xy + wz ) * sx, ( xz - wy ) * sx, 0.0, // 1. column", + " ( xy - wz ) * sy, ( 1.0 - ( xx + zz ) ) * sy, ( yz + wx ) * sy, 0.0, // 2. column", + " ( xz + wy ) * sz, ( yz - wx ) * sz, ( 1.0 - ( xx + yy ) ) * sz, 0.0, // 3. column", + " offset.x, offset.y, offset.z, 1.0);", + " ", + " vec4 mvPosition = modelViewMatrix * (matrix * vec4( position, 1.0 ));", + "", + "\tvColor = color;", + "", + "\tgl_Position = projectionMatrix * mvPosition;", + "", + "\t#include ", + "\t#include ", + "", + "}", + "`;", + "", + "var local_particle_physics_vert = `", + "#define STANDARD", + "varying vec3 vViewPosition;", + "#ifdef USE_TRANSMISSION", + "\tvarying vec3 vWorldPosition;", + "#endif", + "#include ", + "#include ", + "", + "attribute vec3 offset;", + "attribute vec4 rotation;", + "attribute float size;", + "attribute float uvTile;", + "", + "#ifdef UV_TILE", + "uniform vec2 tileCount;", + "#endif", + "", + "#include ", + "#include ", + "#include ", + "#include ", + "#include ", + "#include ", + "#include ", + "#include ", + "#include ", + "", + "void main() {", + " ${uv_vertex_tile}", + "", + " float x2 = rotation.x + rotation.x, y2 = rotation.y + rotation.y, z2 = rotation.z + rotation.z;", + " float xx = rotation.x * x2, xy = rotation.x * y2, xz = rotation.x * z2;", + " float yy = rotation.y * y2, yz = rotation.y * z2, zz = rotation.z * z2;", + " float wx = rotation.w * x2, wy = rotation.w * y2, wz = rotation.w * z2;", + " float sx = size, sy = size, sz = size;", + "", + " mat4 particleMatrix = mat4(( 1.0 - ( yy + zz ) ) * sx, ( xy + wz ) * sx, ( xz - wy ) * sx, 0.0, // 1. column", + " ( xy - wz ) * sy, ( 1.0 - ( xx + zz ) ) * sy, ( yz + wx ) * sy, 0.0, // 2. column", + " ( xz + wy ) * sz, ( yz - wx ) * sz, ( 1.0 - ( xx + yy ) ) * sz, 0.0, // 3. column", + " offset.x, offset.y, offset.z, 1.0);", + "", + "\t#include ", + "\t#include ", + "\t#include ", + "\t#include ", + "\t#include ", + "\t#include ", + "", + "\t// replace defaultnormal_vertex", + "\tvec3 transformedNormal = objectNormal;", + " mat3 m = mat3( particleMatrix );", + " transformedNormal /= vec3( dot( m[ 0 ], m[ 0 ] ), dot( m[ 1 ], m[ 1 ] ), dot( m[ 2 ], m[ 2 ] ) );", + " transformedNormal = m * transformedNormal;", + " transformedNormal = normalMatrix * transformedNormal;", + " #ifdef FLIP_SIDED", + " transformedNormal = - transformedNormal;", + " #endif", + " #ifdef USE_TANGENT", + " vec3 transformedTangent = ( modelViewMatrix * vec4( objectTangent, 0.0 ) ).xyz;", + " #ifdef FLIP_SIDED", + " transformedTangent = - transformedTangent;", + " #endif", + " #endif", + "", + "\t#include ", + "\t#include ", + "\t#include ", + "\t#include ", + "\t#include ", + "", + "\t// replace include ", + " vec4 mvPosition = vec4( transformed, 1.0 );", + " mvPosition = modelViewMatrix * (particleMatrix * mvPosition);", + "\tgl_Position = projectionMatrix * mvPosition;", + "", + "\t#include ", + "\t#include ", + "\t", + "\tvViewPosition = - mvPosition.xyz;", + "\t", + "\t#include ", + "\t#include ", + "\t#include ", + "#ifdef USE_TRANSMISSION", + "\tvWorldPosition = worldPosition.xyz;", + "#endif", + "}", + "`;", + "", + "var stretched_bb_particle_vert = `", + "#include ", + "#include ", + "#include ", + "#include ", + "#include ", + "", + "attribute vec3 offset;", + "attribute float rotation;", + "attribute float size;", + "attribute vec4 velocity;", + "attribute float uvTile;", + "", + "#ifdef UV_TILE", + "uniform vec2 tileCount;", + "#endif", + "", + "uniform float speedFactor;", + "", + "void main() {", + "", + " ${uv_vertex_tile}", + " ", + " float lengthFactor = velocity.w;", + "#ifdef USE_SKEW", + " vec4 mvPosition = modelViewMatrix * vec4( offset, 1.0 );", + " vec3 viewVelocity = normalMatrix * velocity.xyz;", + "", + " vec3 scaledPos = vec3(position.xy * size, position.z);", + " float vlength = length(viewVelocity);", + " vec3 projVelocity = dot(scaledPos, viewVelocity) * viewVelocity / vlength;", + " mvPosition.xyz += scaledPos + projVelocity * (speedFactor / size + lengthFactor / vlength);", + "#else", + " vec4 mvPosition = modelViewMatrix * vec4( offset, 1.0 );", + " vec3 viewVelocity = normalMatrix * velocity.xyz;", + " float vlength = length(viewVelocity); ", + " mvPosition.xyz += position.y * normalize(cross(mvPosition.xyz, viewVelocity)) * size; // switch the cross to match unity implementation", + " mvPosition.xyz -= (position.x + 0.5) * viewVelocity * (1.0 + lengthFactor / vlength) * size; // minus position.x to match unity implementation", + "#endif", + "\tvColor = color;", + "\tgl_Position = projectionMatrix * mvPosition;", + "\t#include ", + "\t#include ", + "}", + "`;", + "", + "function getMaterialUVChannelName(value) {", + " if (value === 0)", + " return 'uv';", + " return `uv${value}`;", + "}", + "", + "new THREE.Vector3(0, 0, 1);", + "class SpriteBatch extends VFXBatch {", + " constructor(settings) {", + " super(settings);", + " this.vector_ = new THREE.Vector3();", + " this.vector2_ = new THREE.Vector3();", + " this.vector3_ = new THREE.Vector3();", + " this.quaternion_ = new THREE.Quaternion();", + " this.quaternion2_ = new THREE.Quaternion();", + " this.quaternion3_ = new THREE.Quaternion();", + " this.rotationMat_ = new THREE.Matrix3();", + " this.rotationMat2_ = new THREE.Matrix3();", + " this.maxParticles = 1000;", + " this.setupBuffers();", + " this.rebuildMaterial();", + " }", + " buildExpandableBuffers() {", + " this.offsetBuffer = new THREE.InstancedBufferAttribute(new Float32Array(this.maxParticles * 3), 3);", + " this.offsetBuffer.setUsage(THREE.DynamicDrawUsage);", + " this.geometry.setAttribute('offset', this.offsetBuffer);", + " this.colorBuffer = new THREE.InstancedBufferAttribute(new Float32Array(this.maxParticles * 4), 4);", + " this.colorBuffer.setUsage(THREE.DynamicDrawUsage);", + " this.geometry.setAttribute('color', this.colorBuffer);", + " if (this.settings.renderMode === RenderMode.Mesh) {", + " this.rotationBuffer = new THREE.InstancedBufferAttribute(new Float32Array(this.maxParticles * 4), 4);", + " this.rotationBuffer.setUsage(THREE.DynamicDrawUsage);", + " this.geometry.setAttribute('rotation', this.rotationBuffer);", + " }", + " else if (this.settings.renderMode === RenderMode.BillBoard ||", + " this.settings.renderMode === RenderMode.HorizontalBillBoard ||", + " this.settings.renderMode === RenderMode.VerticalBillBoard ||", + " this.settings.renderMode === RenderMode.StretchedBillBoard) {", + " this.rotationBuffer = new THREE.InstancedBufferAttribute(new Float32Array(this.maxParticles), 1);", + " this.rotationBuffer.setUsage(THREE.DynamicDrawUsage);", + " this.geometry.setAttribute('rotation', this.rotationBuffer);", + " }", + " this.sizeBuffer = new THREE.InstancedBufferAttribute(new Float32Array(this.maxParticles), 1);", + " this.sizeBuffer.setUsage(THREE.DynamicDrawUsage);", + " this.geometry.setAttribute('size', this.sizeBuffer);", + " this.uvTileBuffer = new THREE.InstancedBufferAttribute(new Float32Array(this.maxParticles), 1);", + " this.uvTileBuffer.setUsage(THREE.DynamicDrawUsage);", + " this.geometry.setAttribute('uvTile', this.uvTileBuffer);", + " if (this.settings.renderMode === RenderMode.StretchedBillBoard) {", + " this.velocityBuffer = new THREE.InstancedBufferAttribute(new Float32Array(this.maxParticles * 4), 4);", + " this.velocityBuffer.setUsage(THREE.DynamicDrawUsage);", + " this.geometry.setAttribute('velocity', this.velocityBuffer);", + " }", + " }", + " setupBuffers() {", + " if (this.geometry)", + " this.geometry.dispose();", + " this.geometry = new THREE.InstancedBufferGeometry();", + " this.geometry.setIndex(this.settings.instancingGeometry.getIndex());", + " if (this.settings.instancingGeometry.hasAttribute('normal')) {", + " this.geometry.setAttribute('normal', this.settings.instancingGeometry.getAttribute('normal'));", + " }", + " this.geometry.setAttribute('position', this.settings.instancingGeometry.getAttribute('position'));", + " this.geometry.setAttribute('uv', this.settings.instancingGeometry.getAttribute('uv'));", + " this.buildExpandableBuffers();", + " }", + " expandBuffers(target) {", + " while (target >= this.maxParticles) {", + " this.maxParticles *= 2;", + " }", + " this.setupBuffers();", + " }", + " rebuildMaterial() {", + " this.layers.mask = this.settings.layers.mask;", + " let uniforms;", + " const defines = {};", + " if (this.settings.material.type === 'MeshStandardMaterial' ||", + " this.settings.material.type === 'MeshPhysicalMaterial') {", + " const mat = this.settings.material;", + " uniforms = THREE.UniformsUtils.merge([", + " THREE.UniformsLib.common,", + " THREE.UniformsLib.envmap,", + " THREE.UniformsLib.aomap,", + " THREE.UniformsLib.lightmap,", + " THREE.UniformsLib.emissivemap,", + " THREE.UniformsLib.bumpmap,", + " THREE.UniformsLib.normalmap,", + " THREE.UniformsLib.displacementmap,", + " THREE.UniformsLib.roughnessmap,", + " THREE.UniformsLib.metalnessmap,", + " THREE.UniformsLib.fog,", + " THREE.UniformsLib.lights,", + " {", + " emissive: { value: new THREE.Color(0x000000) },", + " roughness: { value: 1.0 },", + " metalness: { value: 0.0 },", + " envMapIntensity: { value: 1 },", + " },", + " ]);", + " uniforms['diffuse'].value = mat.color;", + " uniforms['opacity'].value = mat.opacity;", + " uniforms['emissive'].value = mat.emissive;", + " uniforms['roughness'].value = mat.roughness;", + " uniforms['metalness'].value = mat.metalness;", + " if (mat.envMap) {", + " uniforms['envMap'].value = mat.envMap;", + " uniforms['envMapIntensity'].value = mat.envMapIntensity;", + " }", + " if (mat.normalMap) {", + " uniforms['normalMap'].value = mat.normalMap;", + " uniforms['normalScale'].value = mat.normalScale;", + " }", + " if (mat.roughnessMap) {", + " uniforms['roughnessMap'].value = mat.roughnessMap;", + " }", + " if (mat.metalnessMap) {", + " uniforms['metalnessMap'].value = mat.metalnessMap;", + " }", + " if (mat.map) {", + " uniforms['map'] = new THREE.Uniform(mat.map);", + " }", + " }", + " else {", + " uniforms = {};", + " uniforms['map'] = new THREE.Uniform(this.settings.material.map);", + " }", + " if (this.settings.material.alphaTest) {", + " defines['USE_ALPHATEST'] = '';", + " uniforms['alphaTest'] = new THREE.Uniform(this.settings.material.alphaTest);", + " }", + " defines['USE_UV'] = '';", + " const uTileCount = this.settings.uTileCount;", + " const vTileCount = this.settings.vTileCount;", + " if (uTileCount > 1 || vTileCount > 1) {", + " defines['UV_TILE'] = '';", + " uniforms['tileCount'] = new THREE.Uniform(new THREE.Vector2(uTileCount, vTileCount));", + " }", + " if (this.settings.material.defines &&", + " this.settings.material.defines['USE_COLOR_AS_ALPHA'] !== undefined) {", + " defines['USE_COLOR_AS_ALPHA'] = '';", + " }", + " if (this.settings.material.normalMap) {", + " defines['USE_NORMALMAP'] = '';", + " defines['NORMALMAP_UV'] = getMaterialUVChannelName(this.settings.material.normalMap.channel);", + " uniforms['normalMapTransform'] = new THREE.Uniform(new THREE.Matrix3().copy(this.settings.material.normalMap.matrix));", + " }", + " if (this.settings.material.map) {", + " defines['USE_MAP'] = '';", + " defines['MAP_UV'] = getMaterialUVChannelName(this.settings.material.map.channel);", + " uniforms['mapTransform'] = new THREE.Uniform(new THREE.Matrix3().copy(this.settings.material.map.matrix));", + " }", + " defines['USE_COLOR_ALPHA'] = '';", + " let needLights = false;", + " if (this.settings.renderMode === RenderMode.BillBoard ||", + " this.settings.renderMode === RenderMode.VerticalBillBoard ||", + " this.settings.renderMode === RenderMode.HorizontalBillBoard ||", + " this.settings.renderMode === RenderMode.Mesh) {", + " let vertexShader;", + " let fragmentShader;", + " if (this.settings.renderMode === RenderMode.Mesh) {", + " if (this.settings.material.type === 'MeshStandardMaterial' ||", + " this.settings.material.type === 'MeshPhysicalMaterial') {", + " defines['USE_COLOR'] = '';", + " vertexShader = local_particle_physics_vert;", + " fragmentShader = particle_physics_frag;", + " needLights = true;", + " }", + " else {", + " vertexShader = local_particle_vert;", + " fragmentShader = particle_frag;", + " }", + " }", + " else {", + " vertexShader = particle_vert;", + " fragmentShader = particle_frag;", + " }", + " if (this.settings.renderMode === RenderMode.VerticalBillBoard) {", + " defines['VERTICAL'] = '';", + " }", + " else if (this.settings.renderMode === RenderMode.HorizontalBillBoard) {", + " defines['HORIZONTAL'] = '';", + " }", + " this.material = new THREE.ShaderMaterial({", + " uniforms: uniforms,", + " defines: defines,", + " vertexShader: vertexShader,", + " fragmentShader: fragmentShader,", + " transparent: this.settings.material.transparent,", + " depthWrite: !this.settings.material.transparent,", + " blending: this.settings.material.blending,", + " side: this.settings.material.side,", + " alphaTest: this.settings.material.alphaTest,", + " lights: needLights,", + " });", + " }", + " else if (this.settings.renderMode === RenderMode.StretchedBillBoard) {", + " uniforms['speedFactor'] = new THREE.Uniform(1.0);", + " this.material = new THREE.ShaderMaterial({", + " uniforms: uniforms,", + " defines: defines,", + " vertexShader: stretched_bb_particle_vert,", + " fragmentShader: particle_frag,", + " transparent: this.settings.material.transparent,", + " depthWrite: !this.settings.material.transparent,", + " blending: this.settings.material.blending,", + " side: this.settings.material.side,", + " alphaTest: this.settings.material.alphaTest,", + " });", + " }", + " else {", + " throw new Error('render mode unavailable');", + " }", + " }", + " update() {", + " let index = 0;", + " let particleCount = 0;", + " this.systems.forEach((system) => {", + " particleCount += system.particleNum;", + " });", + " if (particleCount > this.maxParticles) {", + " this.expandBuffers(particleCount);", + " }", + " this.systems.forEach((system) => {", + " const particles = system.particles;", + " const particleNum = system.particleNum;", + " const rotation = this.quaternion2_;", + " const translation = this.vector2_;", + " const scale = this.vector3_;", + " system.emitter.matrixWorld.decompose(translation, rotation, scale);", + " this.rotationMat_.setFromMatrix4(system.emitter.matrixWorld);", + " for (let j = 0; j < particleNum; j++ , index++) {", + " const particle = particles[j];", + " if (this.settings.renderMode === RenderMode.Mesh) {", + " let q;", + " if (system.worldSpace) {", + " q = particle.rotation;", + " }", + " else {", + " let parentQ;", + " if (particle.parentMatrix) {", + " parentQ = this.quaternion3_.setFromRotationMatrix(particle.parentMatrix);", + " }", + " else {", + " parentQ = rotation;", + " }", + " q = this.quaternion_;", + " q.copy(particle.rotation).multiply(parentQ);", + " }", + " this.rotationBuffer.setXYZW(index, q.x, q.y, q.z, q.w);", + " }", + " else if (this.settings.renderMode === RenderMode.StretchedBillBoard ||", + " this.settings.renderMode === RenderMode.VerticalBillBoard ||", + " this.settings.renderMode === RenderMode.HorizontalBillBoard ||", + " this.settings.renderMode === RenderMode.BillBoard) {", + " this.rotationBuffer.setX(index, particle.rotation);", + " }", + " let vec;", + " if (system.worldSpace) {", + " vec = particle.position;", + " }", + " else {", + " vec = this.vector_;", + " if (particle.parentMatrix) {", + " vec.copy(particle.position).applyMatrix4(particle.parentMatrix);", + " }", + " else {", + " vec.copy(particle.position).applyMatrix4(system.emitter.matrixWorld);", + " }", + " }", + " this.offsetBuffer.setXYZ(index, vec.x, vec.y, vec.z);", + " this.colorBuffer.setXYZW(index, particle.color.x, particle.color.y, particle.color.z, particle.color.w);", + " if (system.worldSpace) {", + " this.sizeBuffer.setX(index, particle.size);", + " }", + " else {", + " if (particle.parentMatrix) {", + " this.sizeBuffer.setX(index, particle.size);", + " }", + " else {", + " this.sizeBuffer.setX(index, (particle.size * (Math.abs(scale.x) + Math.abs(scale.y) + Math.abs(scale.z))) / 3);", + " }", + " }", + " this.uvTileBuffer.setX(index, particle.uvTile);", + " if (this.settings.renderMode === RenderMode.StretchedBillBoard && this.velocityBuffer) {", + " let speedFactor = system.rendererEmitterSettings.speedFactor;", + " if (speedFactor === 0)", + " speedFactor = 0.001;", + " const lengthFactor = system.rendererEmitterSettings.lengthFactor;", + " let vec;", + " if (system.worldSpace) {", + " vec = particle.velocity;", + " }", + " else {", + " vec = this.vector_;", + " if (particle.parentMatrix) {", + " this.rotationMat2_.setFromMatrix4(particle.parentMatrix);", + " vec.copy(particle.velocity).applyMatrix3(this.rotationMat2_);", + " }", + " else {", + " vec.copy(particle.velocity).applyMatrix3(this.rotationMat_);", + " }", + " }", + " this.velocityBuffer.setXYZW(index, vec.x * speedFactor, vec.y * speedFactor, vec.z * speedFactor, lengthFactor);", + " }", + " }", + " });", + " this.geometry.instanceCount = index;", + " if (index > 0) {", + " this.offsetBuffer.clearUpdateRanges();", + " this.offsetBuffer.addUpdateRange(0, index * 3);", + " this.offsetBuffer.needsUpdate = true;", + " this.sizeBuffer.clearUpdateRanges();", + " this.sizeBuffer.addUpdateRange(0, index);", + " this.sizeBuffer.needsUpdate = true;", + " this.colorBuffer.clearUpdateRanges();", + " this.colorBuffer.addUpdateRange(0, index * 4);", + " this.colorBuffer.needsUpdate = true;", + " this.uvTileBuffer.clearUpdateRanges();", + " this.uvTileBuffer.addUpdateRange(0, index);", + " this.uvTileBuffer.needsUpdate = true;", + " if (this.settings.renderMode === RenderMode.StretchedBillBoard && this.velocityBuffer) {", + " this.velocityBuffer.clearUpdateRanges();", + " this.velocityBuffer.addUpdateRange(0, index * 4);", + " this.velocityBuffer.needsUpdate = true;", + " }", + " if (this.settings.renderMode === RenderMode.Mesh) {", + " this.rotationBuffer.clearUpdateRanges();", + " this.rotationBuffer.addUpdateRange(0, index * 4);", + " this.rotationBuffer.needsUpdate = true;", + " }", + " else if (this.settings.renderMode === RenderMode.StretchedBillBoard ||", + " this.settings.renderMode === RenderMode.HorizontalBillBoard ||", + " this.settings.renderMode === RenderMode.VerticalBillBoard ||", + " this.settings.renderMode === RenderMode.BillBoard) {", + " this.rotationBuffer.clearUpdateRanges();", + " this.rotationBuffer.addUpdateRange(0, index);", + " this.rotationBuffer.needsUpdate = true;", + " }", + " }", + " }", + " dispose() {", + " this.geometry.dispose();", + " }", + "}", + "", + "var trail_frag = `", + "", + "#include ", + "#include ", + "#include ", + "#include ", + "#include ", + "#include ", + "", + "uniform sampler2D alphaMap;", + "uniform float useAlphaMap;", + "uniform float visibility;", + "uniform float alphaTest;", + "uniform vec2 repeat;", + "", + "varying vec4 vColor;", + " ", + "void main() {", + " #include ", + " #include ", + "", + " vec4 c = vColor;", + " ", + " #ifdef USE_MAP", + " #ifdef USE_COLOR_AS_ALPHA", + " vec4 tex = texture2D( map, vUv * repeat );", + " c *= vec4(tex.rgb, tex.r);", + " #else", + " c *= texture2D( map, vUv * repeat );", + " #endif", + " #endif", + " if( useAlphaMap == 1. ) c.a *= texture2D( alphaMap, vUv * repeat ).a;", + " if( c.a < alphaTest ) discard;", + " gl_FragColor = c;", + "", + " #include ", + " #include ", + "}`;", + "", + "var trail_vert = `", + "#include ", + "#include ", + "#include ", + "#include ", + "#include ", + "#include ", + "", + "attribute vec3 previous;", + "attribute vec3 next;", + "attribute float side;", + "attribute float width;", + "", + "uniform vec2 resolution;", + "uniform float lineWidth;", + "uniform float sizeAttenuation;", + " ", + "vec2 fix(vec4 i, float aspect) {", + " vec2 res = i.xy / i.w;", + " res.x *= aspect;", + " return res;", + "}", + " ", + "void main() {", + "", + " ${uv_vertex_tile}", + " ", + " float aspect = resolution.x / resolution.y;", + "", + " vColor = color;", + "", + " mat4 m = projectionMatrix * modelViewMatrix;", + " vec4 finalPosition = m * vec4( position, 1.0 );", + " vec4 prevPos = m * vec4( previous, 1.0 );", + " vec4 nextPos = m * vec4( next, 1.0 );", + "", + " vec2 currentP = fix( finalPosition, aspect );", + " vec2 prevP = fix( prevPos, aspect );", + " vec2 nextP = fix( nextPos, aspect );", + "", + " float w = lineWidth * width;", + "", + " vec2 dir;", + " if( nextP == currentP ) dir = normalize( currentP - prevP );", + " else if( prevP == currentP ) dir = normalize( nextP - currentP );", + " else {", + " vec2 dir1 = normalize( currentP - prevP );", + " vec2 dir2 = normalize( nextP - currentP );", + " dir = normalize( dir1 + dir2 );", + "", + " vec2 perp = vec2( -dir1.y, dir1.x );", + " vec2 miter = vec2( -dir.y, dir.x );", + " //w = clamp( w / dot( miter, perp ), 0., 4., * lineWidth * width );", + "", + " }", + "", + " //vec2 normal = ( cross( vec3( dir, 0. ) vec3( 0., 0., 1. ) ) ).xy;", + " vec4 normal = vec4( -dir.y, dir.x, 0., 1. );", + " normal.xy *= .5 * w;", + " normal *= projectionMatrix;", + " if( sizeAttenuation == 0. ) {", + " normal.xy *= finalPosition.w;", + " normal.xy /= ( vec4( resolution, 0., 1. ) * projectionMatrix ).xy;", + " }", + "", + " finalPosition.xy += normal.xy * side;", + "", + " gl_Position = finalPosition;", + "", + "\t#include ", + "\t#include ", + "\t", + " vec4 mvPosition = modelViewMatrix * vec4( position, 1.0 );", + " ", + "\t#include ", + "}`;", + "", + "new THREE.Vector3(0, 0, 1);", + "class TrailBatch extends VFXBatch {", + " constructor(settings) {", + " super(settings);", + " this.vector_ = new THREE.Vector3();", + " this.vector2_ = new THREE.Vector3();", + " this.vector3_ = new THREE.Vector3();", + " this.quaternion_ = new THREE.Quaternion();", + " this.maxParticles = 10000;", + " this.setupBuffers();", + " this.rebuildMaterial();", + " }", + " setupBuffers() {", + " if (this.geometry)", + " this.geometry.dispose();", + " this.geometry = new THREE.BufferGeometry();", + " this.indexBuffer = new THREE.BufferAttribute(new Uint32Array(this.maxParticles * 6), 1);", + " this.indexBuffer.setUsage(THREE.DynamicDrawUsage);", + " this.geometry.setIndex(this.indexBuffer);", + " this.positionBuffer = new THREE.BufferAttribute(new Float32Array(this.maxParticles * 6), 3);", + " this.positionBuffer.setUsage(THREE.DynamicDrawUsage);", + " this.geometry.setAttribute('position', this.positionBuffer);", + " this.previousBuffer = new THREE.BufferAttribute(new Float32Array(this.maxParticles * 6), 3);", + " this.previousBuffer.setUsage(THREE.DynamicDrawUsage);", + " this.geometry.setAttribute('previous', this.previousBuffer);", + " this.nextBuffer = new THREE.BufferAttribute(new Float32Array(this.maxParticles * 6), 3);", + " this.nextBuffer.setUsage(THREE.DynamicDrawUsage);", + " this.geometry.setAttribute('next', this.nextBuffer);", + " this.widthBuffer = new THREE.BufferAttribute(new Float32Array(this.maxParticles * 2), 1);", + " this.widthBuffer.setUsage(THREE.DynamicDrawUsage);", + " this.geometry.setAttribute('width', this.widthBuffer);", + " this.sideBuffer = new THREE.BufferAttribute(new Float32Array(this.maxParticles * 2), 1);", + " this.sideBuffer.setUsage(THREE.DynamicDrawUsage);", + " this.geometry.setAttribute('side', this.sideBuffer);", + " this.uvBuffer = new THREE.BufferAttribute(new Float32Array(this.maxParticles * 4), 2);", + " this.uvBuffer.setUsage(THREE.DynamicDrawUsage);", + " this.geometry.setAttribute('uv', this.uvBuffer);", + " this.colorBuffer = new THREE.BufferAttribute(new Float32Array(this.maxParticles * 8), 4);", + " this.colorBuffer.setUsage(THREE.DynamicDrawUsage);", + " this.geometry.setAttribute('color', this.colorBuffer);", + " }", + " expandBuffers(target) {", + " while (target >= this.maxParticles) {", + " this.maxParticles *= 2;", + " }", + " this.setupBuffers();", + " }", + " rebuildMaterial() {", + " this.layers.mask = this.settings.layers.mask;", + " const uniforms = {", + " lineWidth: { value: 1 },", + " map: { value: null },", + " useMap: { value: 0 },", + " alphaMap: { value: null },", + " useAlphaMap: { value: 0 },", + " resolution: { value: new THREE.Vector2(1, 1) },", + " sizeAttenuation: { value: 1 },", + " visibility: { value: 1 },", + " alphaTest: { value: 0 },", + " repeat: { value: new THREE.Vector2(1, 1) },", + " };", + " const defines = {};", + " defines['USE_UV'] = '';", + " defines['USE_COLOR_ALPHA'] = '';", + " if (this.settings.material.map) {", + " defines['USE_MAP'] = '';", + " defines['MAP_UV'] = getMaterialUVChannelName(this.settings.material.map.channel);", + " uniforms['map'] = new THREE.Uniform(this.settings.material.map);", + " uniforms['mapTransform'] = new THREE.Uniform(new THREE.Matrix3().copy(this.settings.material.map.matrix));", + " }", + " if (this.settings.material.defines &&", + " this.settings.material.defines['USE_COLOR_AS_ALPHA'] !== undefined) {", + " defines['USE_COLOR_AS_ALPHA'] = '';", + " }", + " if (this.settings.renderMode === RenderMode.Trail) {", + " this.material = new THREE.ShaderMaterial({", + " uniforms: uniforms,", + " defines: defines,", + " vertexShader: trail_vert,", + " fragmentShader: trail_frag,", + " transparent: this.settings.material.transparent,", + " depthWrite: !this.settings.material.transparent,", + " side: this.settings.material.side,", + " blending: this.settings.material.blending || THREE.AdditiveBlending,", + " });", + " }", + " else {", + " throw new Error('render mode unavailable');", + " }", + " }", + " update() {", + " let index = 0;", + " let triangles = 0;", + " let particleCount = 0;", + " this.systems.forEach((system) => {", + " for (let j = 0; j < system.particleNum; j++) {", + " particleCount += system.particles[j].previous.length * 2;", + " }", + " });", + " if (particleCount > this.maxParticles) {", + " this.expandBuffers(particleCount);", + " }", + " this.systems.forEach((system) => {", + " const rotation = this.quaternion_;", + " const translation = this.vector2_;", + " const scale = this.vector3_;", + " system.emitter.matrixWorld.decompose(translation, rotation, scale);", + " const particles = system.particles;", + " const particleNum = system.particleNum;", + " const uTileCount = this.settings.uTileCount;", + " const vTileCount = this.settings.vTileCount;", + " const tileWidth = 1 / uTileCount;", + " const tileHeight = 1 / vTileCount;", + " for (let j = 0; j < particleNum; j++) {", + " const particle = particles[j];", + " const col = particle.uvTile % vTileCount;", + " const row = Math.floor(particle.uvTile / vTileCount + 0.001);", + " const iter = particle.previous.values();", + " let curIter = iter.next();", + " let previous = curIter.value;", + " let current = previous;", + " if (!curIter.done)", + " curIter = iter.next();", + " let next;", + " if (curIter.value !== undefined) {", + " next = curIter.value;", + " }", + " else {", + " next = current;", + " }", + " for (let i = 0; i < particle.previous.length; i++ , index += 2) {", + " this.positionBuffer.setXYZ(index, current.position.x, current.position.y, current.position.z);", + " this.positionBuffer.setXYZ(index + 1, current.position.x, current.position.y, current.position.z);", + " if (system.worldSpace) {", + " this.positionBuffer.setXYZ(index, current.position.x, current.position.y, current.position.z);", + " this.positionBuffer.setXYZ(index + 1, current.position.x, current.position.y, current.position.z);", + " }", + " else {", + " if (particle.parentMatrix) {", + " this.vector_.copy(current.position).applyMatrix4(particle.parentMatrix);", + " }", + " else {", + " this.vector_.copy(current.position).applyMatrix4(system.emitter.matrixWorld);", + " }", + " this.positionBuffer.setXYZ(index, this.vector_.x, this.vector_.y, this.vector_.z);", + " this.positionBuffer.setXYZ(index + 1, this.vector_.x, this.vector_.y, this.vector_.z);", + " }", + " if (system.worldSpace) {", + " this.previousBuffer.setXYZ(index, previous.position.x, previous.position.y, previous.position.z);", + " this.previousBuffer.setXYZ(index + 1, previous.position.x, previous.position.y, previous.position.z);", + " }", + " else {", + " if (particle.parentMatrix) {", + " this.vector_.copy(previous.position).applyMatrix4(particle.parentMatrix);", + " }", + " else {", + " this.vector_.copy(previous.position).applyMatrix4(system.emitter.matrixWorld);", + " }", + " this.previousBuffer.setXYZ(index, this.vector_.x, this.vector_.y, this.vector_.z);", + " this.previousBuffer.setXYZ(index + 1, this.vector_.x, this.vector_.y, this.vector_.z);", + " }", + " if (system.worldSpace) {", + " this.nextBuffer.setXYZ(index, next.position.x, next.position.y, next.position.z);", + " this.nextBuffer.setXYZ(index + 1, next.position.x, next.position.y, next.position.z);", + " }", + " else {", + " if (particle.parentMatrix) {", + " this.vector_.copy(next.position).applyMatrix4(particle.parentMatrix);", + " }", + " else {", + " this.vector_.copy(next.position).applyMatrix4(system.emitter.matrixWorld);", + " }", + " this.nextBuffer.setXYZ(index, this.vector_.x, this.vector_.y, this.vector_.z);", + " this.nextBuffer.setXYZ(index + 1, this.vector_.x, this.vector_.y, this.vector_.z);", + " }", + " this.sideBuffer.setX(index, -1);", + " this.sideBuffer.setX(index + 1, 1);", + " if (system.worldSpace) {", + " this.widthBuffer.setX(index, current.size);", + " this.widthBuffer.setX(index + 1, current.size);", + " }", + " else {", + " if (particle.parentMatrix) {", + " this.widthBuffer.setX(index, current.size);", + " this.widthBuffer.setX(index + 1, current.size);", + " }", + " else {", + " const objectScale = (Math.abs(scale.x) + Math.abs(scale.y) + Math.abs(scale.z)) / 3;", + " this.widthBuffer.setX(index, current.size * objectScale);", + " this.widthBuffer.setX(index + 1, current.size * objectScale);", + " }", + " }", + " this.uvBuffer.setXY(index, (i / particle.previous.length + col) * tileWidth, (vTileCount - row - 1) * tileHeight);", + " this.uvBuffer.setXY(index + 1, (i / particle.previous.length + col) * tileWidth, (vTileCount - row) * tileHeight);", + " this.colorBuffer.setXYZW(index, current.color.x, current.color.y, current.color.z, current.color.w);", + " this.colorBuffer.setXYZW(index + 1, current.color.x, current.color.y, current.color.z, current.color.w);", + " if (i + 1 < particle.previous.length) {", + " this.indexBuffer.setX(triangles * 3, index);", + " this.indexBuffer.setX(triangles * 3 + 1, index + 1);", + " this.indexBuffer.setX(triangles * 3 + 2, index + 2);", + " triangles++;", + " this.indexBuffer.setX(triangles * 3, index + 2);", + " this.indexBuffer.setX(triangles * 3 + 1, index + 1);", + " this.indexBuffer.setX(triangles * 3 + 2, index + 3);", + " triangles++;", + " }", + " previous = current;", + " current = next;", + " if (!curIter.done) {", + " curIter = iter.next();", + " if (curIter.value !== undefined) {", + " next = curIter.value;", + " }", + " }", + " }", + " }", + " });", + " this.positionBuffer.clearUpdateRanges();", + " this.positionBuffer.addUpdateRange(0, index * 3);", + " this.positionBuffer.needsUpdate = true;", + " this.previousBuffer.clearUpdateRanges();", + " this.previousBuffer.addUpdateRange(0, index * 3);", + " this.previousBuffer.needsUpdate = true;", + " this.nextBuffer.clearUpdateRanges();", + " this.nextBuffer.addUpdateRange(0, index * 3);", + " this.nextBuffer.needsUpdate = true;", + " this.sideBuffer.clearUpdateRanges();", + " this.sideBuffer.addUpdateRange(0, index);", + " this.sideBuffer.needsUpdate = true;", + " this.widthBuffer.clearUpdateRanges();", + " this.widthBuffer.addUpdateRange(0, index);", + " this.widthBuffer.needsUpdate = true;", + " this.uvBuffer.clearUpdateRanges();", + " this.uvBuffer.addUpdateRange(0, index * 2);", + " this.uvBuffer.needsUpdate = true;", + " this.colorBuffer.clearUpdateRanges();", + " this.colorBuffer.addUpdateRange(0, index * 4);", + " this.colorBuffer.needsUpdate = true;", + " this.indexBuffer.clearUpdateRanges();", + " this.indexBuffer.addUpdateRange(0, triangles * 3);", + " this.indexBuffer.needsUpdate = true;", + " this.geometry.setDrawRange(0, triangles * 3);", + " }", + " dispose() {", + " this.geometry.dispose();", + " }", + "}", + "", + "class BatchedRenderer extends THREE.Object3D {", + " constructor() {", + " super();", + " this.batches = [];", + " this.systemToBatchIndex = new Map();", + " this.type = 'BatchedRenderer';", + " }", + " static equals(a, b) {", + " return (a.material.side === b.material.side &&", + " a.material.blending === b.material.blending &&", + " a.material.transparent === b.material.transparent &&", + " a.material.type === b.material.type &&", + " a.material.alphaTest === b.material.alphaTest &&", + " a.material.map === b.material.map &&", + " a.renderMode === b.renderMode &&", + " a.uTileCount === b.uTileCount &&", + " a.vTileCount === b.vTileCount &&", + " a.instancingGeometry === b.instancingGeometry &&", + " a.renderOrder === b.renderOrder &&", + " a.layers.mask === b.layers.mask);", + " }", + " addSystem(system) {", + " system._renderer = this;", + " const settings = system.getRendererSettings();", + " for (let i = 0; i < this.batches.length; i++) {", + " if (BatchedRenderer.equals(this.batches[i].settings, settings)) {", + " this.batches[i].addSystem(system);", + " this.systemToBatchIndex.set(system, i);", + " return;", + " }", + " }", + " let batch;", + " switch (settings.renderMode) {", + " case RenderMode.Trail:", + " batch = new TrailBatch(settings);", + " break;", + " case RenderMode.Mesh:", + " case RenderMode.BillBoard:", + " case RenderMode.VerticalBillBoard:", + " case RenderMode.HorizontalBillBoard:", + " case RenderMode.StretchedBillBoard:", + " batch = new SpriteBatch(settings);", + " break;", + " }", + " batch.addSystem(system);", + " this.batches.push(batch);", + " this.systemToBatchIndex.set(system, this.batches.length - 1);", + " this.add(batch);", + " }", + " deleteSystem(system) {", + " const batchIndex = this.systemToBatchIndex.get(system);", + " if (batchIndex != undefined) {", + " this.batches[batchIndex].removeSystem(system);", + " this.systemToBatchIndex.delete(system);", + " }", + " }", + " updateSystem(system) {", + " this.deleteSystem(system);", + " this.addSystem(system);", + " }", + " update(delta) {", + " this.systemToBatchIndex.forEach((value, ps) => {", + " ps.update(delta);", + " });", + " for (let i = 0; i < this.batches.length; i++) {", + " this.batches[i].update();", + " }", + " }", + "}", + "", + "const BatchedParticleRenderer = BatchedRenderer;", + "", + "class QuarksLoader extends THREE.ObjectLoader {", + " constructor(manager) {", + " super(manager);", + " }", + " linkReference(object) {", + " const objectsMap = {};", + " object.traverse(function (child) {", + " objectsMap[child.uuid] = child;", + " });", + " object.traverse(function (child) {", + " if (child.type === 'ParticleEmitter') {", + " const system = child.system;", + " system.emitterShape;", + " for (let i = 0; i < system.behaviors.length; i++) {", + " if (system.behaviors[i] instanceof EmitSubParticleSystem) {", + " system.behaviors[i].subParticleSystem = objectsMap[system.behaviors[i].subParticleSystem];", + " }", + " }", + " }", + " });", + " }", + " parse(json, onLoad) {", + " const object = super.parse(json, onLoad);", + " this.linkReference(object);", + " return object;", + " }", + " parseObject(data, geometries, materials, textures, animations) {", + " let object;", + " function getGeometry(name) {", + " if (geometries[name] === undefined) {", + " console.warn('THREE.ObjectLoader: Undefined geometry', name);", + " }", + " return geometries[name];", + " }", + " function getMaterial(name) {", + " if (name === undefined)", + " return undefined;", + " if (Array.isArray(name)) {", + " const array = [];", + " for (let i = 0, l = name.length; i < l; i++) {", + " const uuid = name[i];", + " if (materials[uuid] === undefined) {", + " console.warn('THREE.ObjectLoader: Undefined material', uuid);", + " }", + " array.push(materials[uuid]);", + " }", + " return array;", + " }", + " if (materials[name] === undefined) {", + " console.warn('THREE.ObjectLoader: Undefined material', name);", + " }", + " return materials[name];", + " }", + " function getTexture(uuid) {", + " if (textures[uuid] === undefined) {", + " console.warn('THREE.ObjectLoader: Undefined texture', uuid);", + " }", + " return textures[uuid];", + " }", + " let geometry, material;", + " const meta = {", + " textures: textures,", + " geometries: geometries,", + " materials: materials,", + " };", + " const dependencies = {};", + " switch (data.type) {", + " case 'ParticleEmitter':", + " object = ParticleSystem.fromJSON(data.ps, meta, dependencies).emitter;", + " break;", + " case 'Scene':", + " object = new THREE.Scene();", + " if (data.background !== undefined) {", + " if (Number.isInteger(data.background)) {", + " object.background = new THREE.Color(data.background);", + " }", + " else {", + " object.background = getTexture(data.background);", + " }", + " }", + " if (data.environment !== undefined) {", + " object.environment = getTexture(data.environment);", + " }", + " if (data.fog !== undefined) {", + " if (data.fog.type === 'Fog') {", + " object.fog = new THREE.Fog(data.fog.color, data.fog.near, data.fog.far);", + " }", + " else if (data.fog.type === 'FogExp2') {", + " object.fog = new THREE.FogExp2(data.fog.color, data.fog.density);", + " }", + " }", + " if (data.backgroundBlurriness !== undefined)", + " object.backgroundBlurriness = data.backgroundBlurriness;", + " break;", + " case 'PerspectiveCamera':", + " object = new THREE.PerspectiveCamera(data.fov, data.aspect, data.near, data.far);", + " if (data.focus !== undefined)", + " object.focus = data.focus;", + " if (data.zoom !== undefined)", + " object.zoom = data.zoom;", + " if (data.filmGauge !== undefined)", + " object.filmGauge = data.filmGauge;", + " if (data.filmOffset !== undefined)", + " object.filmOffset = data.filmOffset;", + " if (data.view !== undefined)", + " object.view = Object.assign({}, data.view);", + " break;", + " case 'OrthographicCamera':", + " object = new THREE.OrthographicCamera(data.left, data.right, data.top, data.bottom, data.near, data.far);", + " if (data.zoom !== undefined)", + " object.zoom = data.zoom;", + " if (data.view !== undefined)", + " object.view = Object.assign({}, data.view);", + " break;", + " case 'AmbientLight':", + " object = new THREE.AmbientLight(data.color, data.intensity);", + " break;", + " case 'DirectionalLight':", + " object = new THREE.DirectionalLight(data.color, data.intensity);", + " break;", + " case 'PointLight':", + " object = new THREE.PointLight(data.color, data.intensity, data.distance, data.decay);", + " break;", + " case 'RectAreaLight':", + " object = new THREE.RectAreaLight(data.color, data.intensity, data.width, data.height);", + " break;", + " case 'SpotLight':", + " object = new THREE.SpotLight(data.color, data.intensity, data.distance, data.angle, data.penumbra, data.decay);", + " break;", + " case 'HemisphereLight':", + " object = new THREE.HemisphereLight(data.color, data.groundColor, data.intensity);", + " break;", + " case 'LightProbe':", + " object = new THREE.LightProbe().fromJSON(data);", + " break;", + " case 'SkinnedMesh':", + " geometry = getGeometry(data.geometry);", + " material = getMaterial(data.material);", + " object = new THREE.SkinnedMesh(geometry, material);", + " if (data.bindMode !== undefined)", + " object.bindMode = data.bindMode;", + " if (data.bindMatrix !== undefined)", + " object.bindMatrix.fromArray(data.bindMatrix);", + " if (data.skeleton !== undefined)", + " object.skeleton = data.skeleton;", + " break;", + " case 'Mesh':", + " geometry = getGeometry(data.geometry);", + " material = getMaterial(data.material);", + " object = new THREE.Mesh(geometry, material);", + " break;", + " case 'InstancedMesh': {", + " geometry = getGeometry(data.geometry);", + " material = getMaterial(data.material);", + " const count = data.count;", + " const instanceMatrix = data.instanceMatrix;", + " const instanceColor = data.instanceColor;", + " object = new THREE.InstancedMesh(geometry, material, count);", + " object.instanceMatrix = new THREE.InstancedBufferAttribute(new Float32Array(instanceMatrix.array), 16);", + " if (instanceColor !== undefined)", + " object.instanceColor = new THREE.InstancedBufferAttribute(new Float32Array(instanceColor.array), instanceColor.itemSize);", + " break;", + " }", + " case 'LOD':", + " object = new THREE.LOD();", + " break;", + " case 'Line':", + " object = new THREE.Line(getGeometry(data.geometry), getMaterial(data.material));", + " break;", + " case 'LineLoop':", + " object = new THREE.LineLoop(getGeometry(data.geometry), getMaterial(data.material));", + " break;", + " case 'LineSegments':", + " object = new THREE.LineSegments(getGeometry(data.geometry), getMaterial(data.material));", + " break;", + " case 'PointCloud':", + " case 'Points':", + " object = new THREE.Points(getGeometry(data.geometry), getMaterial(data.material));", + " break;", + " case 'Sprite':", + " object = new THREE.Sprite(getMaterial(data.material));", + " break;", + " case 'Group':", + " object = new THREE.Group();", + " break;", + " case 'Bone':", + " object = new THREE.Bone();", + " break;", + " default:", + " object = new THREE.Object3D();", + " }", + " object.uuid = data.uuid;", + " if (data.name !== undefined)", + " object.name = data.name;", + " if (data.matrix !== undefined) {", + " object.matrix.fromArray(data.matrix);", + " if (data.matrixAutoUpdate !== undefined)", + " object.matrixAutoUpdate = data.matrixAutoUpdate;", + " if (object.matrixAutoUpdate)", + " object.matrix.decompose(object.position, object.quaternion, object.scale);", + " }", + " else {", + " if (data.position !== undefined)", + " object.position.fromArray(data.position);", + " if (data.rotation !== undefined)", + " object.rotation.fromArray(data.rotation);", + " if (data.quaternion !== undefined)", + " object.quaternion.fromArray(data.quaternion);", + " if (data.scale !== undefined)", + " object.scale.fromArray(data.scale);", + " }", + " if (data.castShadow !== undefined)", + " object.castShadow = data.castShadow;", + " if (data.receiveShadow !== undefined)", + " object.receiveShadow = data.receiveShadow;", + " if (data.shadow) {", + " if (data.shadow.bias !== undefined)", + " object.shadow.bias = data.shadow.bias;", + " if (data.shadow.normalBias !== undefined)", + " object.normalBias = data.shadow.normalBias;", + " if (data.shadow.radius !== undefined)", + " object.radius = data.shadow.radius;", + " if (data.shadow.mapSize !== undefined)", + " object.mapSize.fromArray(data.shadow.mapSize);", + " if (data.shadow.camera !== undefined) {", + " object.camera = this.parseObject(data.shadow.camera);", + " }", + " }", + " if (data.visible !== undefined)", + " object.visible = data.visible;", + " if (data.frustumCulled !== undefined)", + " object.frustumCulled = data.frustumCulled;", + " if (data.renderOrder !== undefined)", + " object.renderOrder = data.renderOrder;", + " if (data.userData !== undefined)", + " object.userData = data.userData;", + " if (data.layers !== undefined)", + " object.layers.mask = data.layers;", + " if (data.children !== undefined) {", + " const children = data.children;", + " for (let i = 0; i < children.length; i++) {", + " object.add(this.parseObject(children[i], geometries, materials, textures, animations));", + " }", + " }", + " if (data.animations !== undefined) {", + " const objectAnimations = data.animations;", + " for (let i = 0; i < objectAnimations.length; i++) {", + " const uuid = objectAnimations[i];", + " object.animations.push(animations[uuid]);", + " }", + " }", + " if (data.type === 'LOD') {", + " if (data.autoUpdate !== undefined)", + " object.autoUpdate = data.autoUpdate;", + " const levels = data.levels;", + " for (let l = 0; l < levels.length; l++) {", + " const level = levels[l];", + " const child = object.getObjectByProperty('uuid', level.object);", + " if (child !== undefined) {", + " object.addLevel(child, level.distance);", + " }", + " }", + " }", + " return object;", + " }", + "}", + "", + "const Plugins = [];", + "function loadPlugin(plugin) {", + " const existing = Plugins.find((item) => item.id === plugin.id);", + " if (!existing) {", + " plugin.initialize();", + " for (const emitterShape of plugin.emitterShapes) {", + " if (!EmitterShapes[emitterShape.type]) {", + " EmitterShapes[emitterShape.type] = emitterShape;", + " }", + " }", + " for (const behavior of plugin.behaviors) {", + " if (!BehaviorTypes[behavior.type]) {", + " BehaviorTypes[behavior.type] = behavior;", + " }", + " }", + " }", + "}", + "function unloadPlugin(pluginId) {", + " const plugin = Plugins.find((item) => item.id === pluginId);", + " if (plugin) {", + " for (const emitterShape of plugin.emitterShapes) {", + " if (EmitterShapes[emitterShape.type]) {", + " delete EmitterShapes[emitterShape.type];", + " }", + " }", + " for (const behavior of plugin.behaviors) {", + " if (BehaviorTypes[behavior.type]) {", + " delete BehaviorTypes[behavior.type];", + " }", + " }", + " }", + "}", + "", + "let NodeValueType = void 0;", + "(function (NodeValueType) {", + " NodeValueType[NodeValueType[\"Number\"] = 0] = \"Number\";", + " NodeValueType[NodeValueType[\"Vec2\"] = 1] = \"Vec2\";", + " NodeValueType[NodeValueType[\"Vec3\"] = 2] = \"Vec3\";", + " NodeValueType[NodeValueType[\"Vec4\"] = 3] = \"Vec4\";", + " NodeValueType[NodeValueType[\"Boolean\"] = 4] = \"Boolean\";", + " NodeValueType[NodeValueType[\"AnyType\"] = 5] = \"AnyType\";", + " NodeValueType[NodeValueType[\"NullableAnyType\"] = 6] = \"NullableAnyType\";", + " NodeValueType[NodeValueType[\"EventStream\"] = 7] = \"EventStream\";", + "})(NodeValueType || (NodeValueType = {}));", + "const genDefaultForNodeValueType = (type) => {", + " switch (type) {", + " case NodeValueType.Boolean:", + " return false;", + " case NodeValueType.Number:", + " return 0;", + " case NodeValueType.Vec2:", + " return new THREE.Vector2();", + " case NodeValueType.Vec3:", + " return new THREE.Vector3();", + " case NodeValueType.Vec4:", + " return new THREE.Vector4();", + " case NodeValueType.AnyType:", + " return 0;", + " case NodeValueType.NullableAnyType:", + " return undefined;", + " }", + "};", + "", + "class Node {", + " constructor(type, signatureIndex = -1, data = {}) {", + " this.inputs = [];", + " this.outputs = [];", + " this.signatureIndex = -1;", + " this.position = new THREE.Vector2();", + " this.outputValues = [];", + " this.id = '' + Math.round(Math.random() * 100000);", + " this.type = type;", + " this.signatureIndex = signatureIndex;", + " this.data = data;", + " const realIndex = signatureIndex === -1 ? 0 : signatureIndex;", + " for (let i = 0; i < type.nodeTypeSignatures[realIndex].inputTypes.length; i++) {", + " this.inputs.push(undefined);", + " }", + " for (let i = 0; i < type.nodeTypeSignatures[realIndex].outputTypes.length; i++) {", + " this.outputs.push([]);", + " this.outputValues.push(genDefaultForNodeValueType(type.nodeTypeSignatures[realIndex].outputTypes[i]));", + " }", + " }", + " get inputTypes() {", + " const signatureIndex = this.signatureIndex === -1 ? 0 : this.signatureIndex;", + " return this.type.nodeTypeSignatures[signatureIndex].inputTypes;", + " }", + " get outputTypes() {", + " const signatureIndex = this.signatureIndex === -1 ? 0 : this.signatureIndex;", + " return this.type.nodeTypeSignatures[signatureIndex].outputTypes;", + " }", + " func(context, inputs, outputs) {", + " const signatureIndex = this.signatureIndex === -1 ? 0 : this.signatureIndex;", + " this.type.nodeTypeSignatures[signatureIndex].func(context, this.data, inputs, outputs);", + " }", + "}", + "class Wire {", + " constructor(input, inputIndex, output, outputIndex) {", + " this.input = input;", + " this.inputIndex = inputIndex;", + " this.input.outputs[inputIndex].push(this);", + " this.output = output;", + " this.outputIndex = outputIndex;", + " this.output.inputs[outputIndex] = this;", + " }", + "}", + "", + "class BaseCompiler {", + " constructor() {", + " this.visited = new Set();", + " BaseCompiler.Instance = this;", + " }", + " buildExecutionOrder(graph, context) {", + " graph.nodesInOrder.length = 0;", + " this.visited.clear();", + " for (let i = 0; i < graph.outputNodes.length; i++) {", + " const node = graph.outputNodes[i];", + " if (node.inputs[0] !== undefined) {", + " this._traverse(node, graph, context);", + " }", + " }", + " graph.compiled = true;", + " }", + " _traverse(node, graph, context) {", + " this.visited.add(node.id);", + " for (let i = 0; i < node.inputs.length; i++) {", + " if (node.inputs[i] instanceof Wire) {", + " const inputNode = node.inputs[i].input;", + " if (!this.visited.has(inputNode.id)) {", + " this._traverse(inputNode, graph, context);", + " }", + " }", + " else if (node.inputs[i] !== undefined);", + " else;", + " }", + " graph.nodesInOrder.push(node);", + " }", + "}", + "", + "class Interpreter extends BaseCompiler {", + " constructor() {", + " super();", + " }", + " executeCompiledGraph(graph, context) {", + " const nodes = graph.nodesInOrder;", + " for (let i = 0; i < nodes.length; i++) {", + " const inputValues = [];", + " const node = nodes[i];", + " for (let j = 0; j < node.inputs.length; j++) {", + " if (node.inputs[j] instanceof Wire) {", + " inputValues.push(node.inputs[j].input.outputValues[node.inputs[j].inputIndex]);", + " }", + " else if (node.inputs[j] !== undefined) {", + " inputValues.push(node.inputs[j].getValue(context));", + " }", + " else {", + " inputValues.push(undefined);", + " }", + " }", + " node.func(context, inputValues, node.outputValues);", + " }", + " }", + " run(graph, context) {", + " if (!graph.compiled) {", + " this.buildExecutionOrder(graph, context);", + " }", + " this.executeCompiledGraph(graph, context);", + " }", + "}", + "", + "class NodeType {", + " constructor(name) {", + " this.nodeTypeSignatures = [];", + " this.name = name;", + " }", + " addSignature(inputTypes, outputTypes, func) {", + " this.nodeTypeSignatures.push({", + " inputTypes: inputTypes,", + " outputTypes: outputTypes,", + " func: func,", + " });", + " }", + "}", + "class GraphNodeType extends NodeType {", + " constructor(nodeGraph) {", + " const inputTypes = [];", + " for (let i = 0; i < nodeGraph.inputNodes.length; i++) {", + " if (nodeGraph.inputNodes[i].type.name === 'input') {", + " inputTypes.push(nodeGraph.inputNodes[i].data.type);", + " }", + " }", + " const outputTypes = [];", + " for (let i = 0; i < nodeGraph.outputNodes.length; i++) {", + " if (nodeGraph.outputNodes[i].type.name === 'output') {", + " outputTypes.push(nodeGraph.outputNodes[i].data.type);", + " }", + " }", + " super(nodeGraph.name);", + " this.addSignature(inputTypes, outputTypes, (context, data, inputs, outputs) => {", + " context.inputs = inputs;", + " context.outputs = outputs;", + " Interpreter.Instance.run(nodeGraph, context);", + " });", + " this.nodeGraph = nodeGraph;", + " }", + "}", + "", + "const NodeTypes = {};", + "const addNode = new NodeType('add');", + "addNode.addSignature([NodeValueType.Number, NodeValueType.Number], [NodeValueType.Number], (context, data, inputs, outputs) => {", + " outputs[0] = inputs[0] + inputs[1];", + "});", + "addNode.addSignature([NodeValueType.Vec2, NodeValueType.Vec2], [NodeValueType.Vec2], (context, data, inputs, outputs) => {", + " outputs[0].addVectors(inputs[0], inputs[1]);", + "});", + "addNode.addSignature([NodeValueType.Vec3, NodeValueType.Vec3], [NodeValueType.Vec3], (context, data, inputs, outputs) => {", + " outputs[0].addVectors(inputs[0], inputs[1]);", + "});", + "addNode.addSignature([NodeValueType.Vec4, NodeValueType.Vec4], [NodeValueType.Vec4], (context, data, inputs, outputs) => {", + " outputs[0].addVectors(inputs[0], inputs[1]);", + "});", + "NodeTypes['add'] = addNode;", + "const subNode = new NodeType('sub');", + "subNode.addSignature([NodeValueType.Number, NodeValueType.Number], [NodeValueType.Number], (context, data, inputs, outputs) => {", + " outputs[0] = inputs[0] - inputs[1];", + "});", + "subNode.addSignature([NodeValueType.Vec2, NodeValueType.Vec2], [NodeValueType.Vec2], (context, data, inputs, outputs) => {", + " outputs[0].subVectors(inputs[0], inputs[1]);", + "});", + "subNode.addSignature([NodeValueType.Vec3, NodeValueType.Vec3], [NodeValueType.Vec3], (context, data, inputs, outputs) => {", + " outputs[0].subVectors(inputs[0], inputs[1]);", + "});", + "subNode.addSignature([NodeValueType.Vec4, NodeValueType.Vec4], [NodeValueType.Vec4], (context, data, inputs, outputs) => {", + " outputs[0].subVectors(inputs[0], inputs[1]);", + "});", + "NodeTypes['sub'] = subNode;", + "const mulNode = new NodeType('mul');", + "mulNode.addSignature([NodeValueType.Number, NodeValueType.Number], [NodeValueType.Number], (context, data, inputs, outputs) => {", + " outputs[0] = inputs[0] * inputs[1];", + "});", + "mulNode.addSignature([NodeValueType.Vec2, NodeValueType.Number], [NodeValueType.Vec2], (context, data, inputs, outputs) => {", + " outputs[0].copy(inputs[0]).multiplyScalar(inputs[1]);", + "});", + "mulNode.addSignature([NodeValueType.Vec3, NodeValueType.Number], [NodeValueType.Vec3], (context, data, inputs, outputs) => {", + " outputs[0].copy(inputs[0]).multiplyScalar(inputs[1]);", + "});", + "mulNode.addSignature([NodeValueType.Vec4, NodeValueType.Number], [NodeValueType.Vec4], (context, data, inputs, outputs) => {", + " outputs[0].copy(inputs[0]).multiplyScalar(inputs[1]);", + "});", + "NodeTypes['mul'] = mulNode;", + "const divNode = new NodeType('div');", + "divNode.addSignature([NodeValueType.Number, NodeValueType.Number], [NodeValueType.Number], (context, data, inputs, outputs) => {", + " outputs[0] = inputs[0] / inputs[1];", + "});", + "divNode.addSignature([NodeValueType.Vec2, NodeValueType.Number], [NodeValueType.Vec2], (context, data, inputs, outputs) => {", + " outputs[0].copy(inputs[0]).divideScalar(inputs[1]);", + "});", + "divNode.addSignature([NodeValueType.Vec3, NodeValueType.Number], [NodeValueType.Vec3], (context, data, inputs, outputs) => {", + " outputs[0].copy(inputs[0]).divideScalar(inputs[1]);", + "});", + "divNode.addSignature([NodeValueType.Vec4, NodeValueType.Number], [NodeValueType.Vec4], (context, data, inputs, outputs) => {", + " outputs[0].copy(inputs[0]).divideScalar(inputs[1]);", + "});", + "NodeTypes['div'] = divNode;", + "const sinNode = new NodeType('sin');", + "sinNode.addSignature([NodeValueType.Number], [NodeValueType.Number], (context, data, inputs, outputs) => {", + " outputs[0] = Math.sin(inputs[0]);", + "});", + "NodeTypes['sin'] = sinNode;", + "const cosNode = new NodeType('cos');", + "cosNode.addSignature([NodeValueType.Number], [NodeValueType.Number], (context, data, inputs, outputs) => {", + " outputs[0] = Math.cos(inputs[0]);", + "});", + "NodeTypes['cos'] = cosNode;", + "const tanNode = new NodeType('tan');", + "tanNode.addSignature([NodeValueType.Number], [NodeValueType.Number], (context, data, inputs, outputs) => {", + " outputs[0] = Math.tan(inputs[0]);", + "});", + "NodeTypes['tan'] = tanNode;", + "const absNode = new NodeType('abs');", + "absNode.addSignature([NodeValueType.Number], [NodeValueType.Number], (context, data, inputs, outputs) => {", + " outputs[0] = Math.abs(inputs[0]);", + "});", + "NodeTypes['abs'] = absNode;", + "const minNode = new NodeType('min');", + "minNode.addSignature([NodeValueType.Number, NodeValueType.Number], [NodeValueType.Number], (context, data, inputs, outputs) => {", + " outputs[0] = Math.min(inputs[0], inputs[1]);", + "});", + "NodeTypes['min'] = minNode;", + "const maxNode = new NodeType('max');", + "maxNode.addSignature([NodeValueType.Number, NodeValueType.Number], [NodeValueType.Number], (context, data, inputs, outputs) => {", + " outputs[0] = Math.max(inputs[0], inputs[1]);", + "});", + "NodeTypes['max'] = maxNode;", + "const dot = new NodeType('dot');", + "dot.addSignature([NodeValueType.Vec2, NodeValueType.Vec2], [NodeValueType.Number], (context, data, inputs, outputs) => {", + " outputs[0] = inputs[0].dot(inputs[1]);", + "});", + "dot.addSignature([NodeValueType.Vec3, NodeValueType.Vec3], [NodeValueType.Number], (context, data, inputs, outputs) => {", + " outputs[0] = inputs[0].dot(inputs[1]);", + "});", + "dot.addSignature([NodeValueType.Vec4, NodeValueType.Vec4], [NodeValueType.Number], (context, data, inputs, outputs) => {", + " outputs[0] = inputs[0].dot(inputs[1]);", + "});", + "NodeTypes['dot'] = dot;", + "const cross = new NodeType('cross');", + "cross.addSignature([NodeValueType.Vec3, NodeValueType.Vec3], [NodeValueType.Vec3], (context, data, inputs, outputs) => {", + " outputs[0].crossVectors(inputs[0], inputs[1]);", + "});", + "NodeTypes['cross'] = cross;", + "const length = new NodeType('length');", + "length.addSignature([NodeValueType.Vec2], [NodeValueType.Number], (context, data, inputs, outputs) => {", + " outputs[0] = inputs[0].length();", + "});", + "length.addSignature([NodeValueType.Vec3], [NodeValueType.Number], (context, data, inputs, outputs) => {", + " outputs[0] = inputs[0].length();", + "});", + "length.addSignature([NodeValueType.Vec4], [NodeValueType.Number], (context, data, inputs, outputs) => {", + " outputs[0] = inputs[0].length();", + "});", + "NodeTypes['length'] = length;", + "const lengthSq = new NodeType('lengthSq');", + "lengthSq.addSignature([NodeValueType.Vec2], [NodeValueType.Number], (context, data, inputs, outputs) => {", + " outputs[0] = inputs[0].lengthSq();", + "});", + "lengthSq.addSignature([NodeValueType.Vec3], [NodeValueType.Number], (context, data, inputs, outputs) => {", + " outputs[0] = inputs[0].lengthSq();", + "});", + "lengthSq.addSignature([NodeValueType.Vec4], [NodeValueType.Number], (context, data, inputs, outputs) => {", + " outputs[0] = inputs[0].lengthSq();", + "});", + "NodeTypes['lengthSq'] = lengthSq;", + "const normalize = new NodeType('normalize');", + "normalize.addSignature([NodeValueType.Vec2], [NodeValueType.Vec2], (context, data, inputs, outputs) => {", + " outputs[0].copy(inputs[0]).normalize();", + "});", + "normalize.addSignature([NodeValueType.Vec3], [NodeValueType.Vec3], (context, data, inputs, outputs) => {", + " outputs[0].copy(inputs[0]).normalize();", + "});", + "normalize.addSignature([NodeValueType.Vec4], [NodeValueType.Vec4], (context, data, inputs, outputs) => {", + " outputs[0].copy(inputs[0]).normalize();", + "});", + "NodeTypes['normalize'] = normalize;", + "const distance = new NodeType('distance');", + "distance.addSignature([NodeValueType.Vec2, NodeValueType.Vec2], [NodeValueType.Number], (context, data, inputs, outputs) => {", + " outputs[0] = inputs[0].distanceTo(inputs[1]);", + "});", + "distance.addSignature([NodeValueType.Vec3, NodeValueType.Vec3], [NodeValueType.Number], (context, data, inputs, outputs) => {", + " outputs[0] = inputs[0].distanceTo(inputs[1]);", + "});", + "NodeTypes['distance'] = distance;", + "const andNode = new NodeType('and');", + "andNode.addSignature([NodeValueType.Boolean, NodeValueType.Boolean], [NodeValueType.Boolean], (context, data, inputs, outputs) => {", + " outputs[0] = inputs[0] && inputs[1];", + "});", + "NodeTypes['and'] = andNode;", + "const orNode = new NodeType('or');", + "orNode.addSignature([NodeValueType.Boolean, NodeValueType.Boolean], [NodeValueType.Boolean], (context, data, inputs, outputs) => {", + " outputs[0] = inputs[0] || inputs[1];", + "});", + "NodeTypes['or'] = orNode;", + "const notNode = new NodeType('not');", + "notNode.addSignature([NodeValueType.Boolean], [NodeValueType.Boolean], (context, data, inputs, outputs) => {", + " outputs[0] = !inputs[0];", + "});", + "NodeTypes['not'] = notNode;", + "const equalNode = new NodeType('equal');", + "equalNode.addSignature([NodeValueType.Number, NodeValueType.Number], [NodeValueType.Boolean], (context, data, inputs, outputs) => {", + " outputs[0] = inputs[0] === inputs[1];", + "});", + "equalNode.addSignature([NodeValueType.Vec2, NodeValueType.Vec2], [NodeValueType.Boolean], (context, data, inputs, outputs) => {", + " outputs[0] = inputs[0].equals(inputs[1]);", + "});", + "equalNode.addSignature([NodeValueType.Vec3, NodeValueType.Vec3], [NodeValueType.Boolean], (context, data, inputs, outputs) => {", + " outputs[0] = inputs[0].equals(inputs[1]);", + "});", + "equalNode.addSignature([NodeValueType.Vec4, NodeValueType.Vec4], [NodeValueType.Boolean], (context, data, inputs, outputs) => {", + " outputs[0] = inputs[0].equals(inputs[1]);", + "});", + "NodeTypes['equal'] = equalNode;", + "const lessThanNode = new NodeType('lessThan');", + "lessThanNode.addSignature([NodeValueType.Number, NodeValueType.Number], [NodeValueType.Boolean], (context, data, inputs, outputs) => {", + " outputs[0] = inputs[0] < inputs[1];", + "});", + "NodeTypes['lessThan'] = lessThanNode;", + "const greaterThanNode = new NodeType('greaterThan');", + "greaterThanNode.addSignature([NodeValueType.Number, NodeValueType.Number], [NodeValueType.Boolean], (context, data, inputs, outputs) => {", + " outputs[0] = inputs[0] > inputs[1];", + "});", + "NodeTypes['greaterThan'] = greaterThanNode;", + "const lessThanOrEqualNode = new NodeType('lessThanOrEqual');", + "lessThanOrEqualNode.addSignature([NodeValueType.Number, NodeValueType.Number], [NodeValueType.Boolean], (context, data, inputs, outputs) => {", + " outputs[0] = inputs[0] <= inputs[1];", + "});", + "NodeTypes['lessThanOrEqual'] = lessThanOrEqualNode;", + "const greaterThanOrEqualNode = new NodeType('greaterThanOrEqual');", + "greaterThanOrEqualNode.addSignature([NodeValueType.Number, NodeValueType.Number], [NodeValueType.Boolean], (context, data, inputs, outputs) => {", + " outputs[0] = inputs[0] >= inputs[1];", + "});", + "NodeTypes['greaterThanOrEqual'] = greaterThanOrEqualNode;", + "const ifNode = new NodeType('if');", + "ifNode.addSignature([NodeValueType.Boolean, NodeValueType.AnyType, NodeValueType.AnyType], [NodeValueType.AnyType], (context, data, inputs, outputs) => {", + " outputs[0] = inputs[0] ? inputs[1] : inputs[2];", + "});", + "NodeTypes['if'] = ifNode;", + "const numberNode = new NodeType('number');", + "numberNode.addSignature([], [NodeValueType.Number], (context, data, inputs, outputs) => {", + " outputs[0] = data.value;", + "});", + "NodeTypes['number'] = numberNode;", + "const vec2Node = new NodeType('vec2');", + "vec2Node.addSignature([NodeValueType.Number, NodeValueType.Number], [NodeValueType.Vec2], (context, data, inputs, outputs) => {", + " outputs[0].x = inputs[0];", + " outputs[0].y = inputs[1];", + "});", + "NodeTypes['vec2'] = vec2Node;", + "const vec3Node = new NodeType('vec3');", + "vec3Node.addSignature([NodeValueType.Number, NodeValueType.Number, NodeValueType.Number], [NodeValueType.Vec3], (context, data, inputs, outputs) => {", + " outputs[0].x = inputs[0];", + " outputs[0].y = inputs[1];", + " outputs[0].z = inputs[2];", + "});", + "NodeTypes['vec3'] = vec3Node;", + "const vec4Node = new NodeType('vec4');", + "vec4Node.addSignature([NodeValueType.Number, NodeValueType.Number, NodeValueType.Number, NodeValueType.Number], [NodeValueType.Vec4], (context, data, inputs, outputs) => {", + " outputs[0].x = inputs[0];", + " outputs[0].y = inputs[1];", + " outputs[0].z = inputs[2];", + " outputs[0].w = inputs[3];", + "});", + "NodeTypes['vec4'] = vec4Node;", + "const splitVec2Node = new NodeType('splitVec2');", + "splitVec2Node.addSignature([NodeValueType.Vec2], [NodeValueType.Number, NodeValueType.Number], (context, data, inputs, outputs) => {", + " outputs[0] = inputs[0].x;", + " outputs[1] = inputs[0].y;", + "});", + "NodeTypes['splitVec2'] = splitVec2Node;", + "const splitVec3Node = new NodeType('splitVec3');", + "splitVec3Node.addSignature([NodeValueType.Vec3], [NodeValueType.Number, NodeValueType.Number, NodeValueType.Number], (context, data, inputs, outputs) => {", + " outputs[0] = inputs[0].x;", + " outputs[1] = inputs[0].y;", + " outputs[2] = inputs[0].z;", + "});", + "NodeTypes['splitVec3'] = splitVec3Node;", + "const splitVec4Node = new NodeType('splitVec4');", + "splitVec4Node.addSignature([NodeValueType.Vec4], [NodeValueType.Number, NodeValueType.Number, NodeValueType.Number, NodeValueType.Number], (context, data, inputs, outputs) => {", + " outputs[0] = inputs[0].x;", + " outputs[1] = inputs[0].y;", + " outputs[2] = inputs[0].z;", + " outputs[3] = inputs[0].w;", + "});", + "NodeTypes['splitVec4'] = splitVec4Node;", + "const boolNode = new NodeType('bool');", + "boolNode.addSignature([], [NodeValueType.Boolean], (context, data, inputs, outputs) => {", + " outputs[0] = data.value;", + "});", + "NodeTypes['bool'] = boolNode;", + "const particlePropertyNode = new NodeType('particleProperty');", + "particlePropertyNode.addSignature([NodeValueType.NullableAnyType], [NodeValueType.NullableAnyType], (context, data, inputs, outputs) => {", + " if (inputs[0] !== undefined) {", + " if (typeof inputs[0] === 'object') {", + " context.particle[data.property].copy(inputs[0]);", + " }", + " else {", + " context.particle[data.property] = inputs[0];", + " }", + " }", + " if (context.particle[data.property] !== undefined) {", + " if (typeof outputs[0] === 'object') {", + " outputs[0].copy(context.particle[data.property]);", + " }", + " else {", + " outputs[0] = context.particle[data.property];", + " }", + " }", + "});", + "NodeTypes['particleProperty'] = particlePropertyNode;", + "const emitNode = new NodeType('emit');", + "emitNode.addSignature([NodeValueType.EventStream], [], (context, data, inputs, outputs) => {", + " const arr = inputs[0];", + " for (let i = 0; i < arr.length; i++) {", + " context.signal(i, arr[i]);", + " }", + "});", + "NodeTypes['emit'] = emitNode;", + "const graphPropertyNode = new NodeType('graphProperty');", + "graphPropertyNode.addSignature([NodeValueType.NullableAnyType], [NodeValueType.NullableAnyType], (context, data, inputs, outputs) => {", + " if (inputs[0] !== undefined) {", + " if (typeof inputs[0] === 'object') {", + " context.graph[data.property].copy(inputs[0]);", + " }", + " else {", + " context.graph[data.property] = inputs[0];", + " }", + " }", + " if (context.graph[data.property] !== undefined) {", + " if (typeof outputs[0] === 'object') {", + " outputs[0].copy(context.graph[data.property]);", + " }", + " else {", + " outputs[0] = context.graph[data.property];", + " }", + " }", + "});", + "NodeTypes['graphProperty'] = graphPropertyNode;", + "const startEventNode = new NodeType('startEvent');", + "startEventNode.addSignature([], [NodeValueType.EventStream], (context, data, inputs, outputs) => {", + " outputs[0] = [{ type: 'start' }];", + "});", + "NodeTypes['startEvent'] = startEventNode;", + "const repeaterNode = new NodeType('repeater');", + "repeaterNode.addSignature([NodeValueType.EventStream, NodeValueType.Number], [NodeValueType.EventStream], (context, data, inputs, outputs) => {", + " const arr = inputs[0];", + " const count = inputs[1];", + " const result = [];", + " for (let j = 0; j < arr.length; j++) {", + " for (let i = 0; i < count; i++) {", + " result.push(arr[j]);", + " }", + " }", + " outputs[0] = result;", + "});", + "NodeTypes['repeater'] = repeaterNode;", + "const timeNode = new NodeType('time');", + "timeNode.addSignature([], [NodeValueType.Number], (context, data, inputs, outputs) => {", + " outputs[0] = context.emissionState.time;", + "});", + "NodeTypes['time'] = timeNode;", + "const deltaNode = new NodeType('delta');", + "deltaNode.addSignature([], [NodeValueType.Number], (context, data, inputs, outputs) => {", + " outputs[0] = context.delta;", + "});", + "NodeTypes['delta'] = deltaNode;", + "const outputNode = new NodeType('output');", + "outputNode.addSignature([NodeValueType.Number], [NodeValueType.Number], (context, data, inputs, outputs) => {", + " outputs[0] = inputs[0];", + "});", + "outputNode.addSignature([NodeValueType.Vec2], [NodeValueType.Vec2], (context, data, inputs, outputs) => {", + " outputs[0] = inputs[0];", + "});", + "outputNode.addSignature([NodeValueType.Vec3], [NodeValueType.Vec3], (context, data, inputs, outputs) => {", + " outputs[0] = inputs[0];", + "});", + "outputNode.addSignature([NodeValueType.Vec4], [NodeValueType.Vec4], (context, data, inputs, outputs) => {", + " outputs[0] = inputs[0];", + "});", + "outputNode.addSignature([NodeValueType.Boolean], [NodeValueType.Boolean], (context, data, inputs, outputs) => {", + " outputs[0] = inputs[0];", + "});", + "NodeTypes['output'] = outputNode;", + "const lerpNode = new NodeType('lerp');", + "lerpNode.addSignature([NodeValueType.Number, NodeValueType.Number, NodeValueType.Number], [NodeValueType.Number], (context, data, inputs, outputs) => {", + " outputs[0] =", + " inputs[0] * (1 - inputs[2]) + inputs[1] * inputs[2];", + "});", + "lerpNode.addSignature([NodeValueType.Vec2, NodeValueType.Vec2, NodeValueType.Number], [NodeValueType.Vec2], (context, data, inputs, outputs) => {", + " outputs[0].lerpVectors(inputs[0], inputs[1], inputs[2]);", + "});", + "lerpNode.addSignature([NodeValueType.Vec3, NodeValueType.Vec3, NodeValueType.Number], [NodeValueType.Vec3], (context, data, inputs, outputs) => {", + " outputs[0].lerpVectors(inputs[0], inputs[1], inputs[2]);", + "});", + "lerpNode.addSignature([NodeValueType.Vec4, NodeValueType.Vec4, NodeValueType.Number], [NodeValueType.Vec4], (context, data, inputs, outputs) => {", + " outputs[0].lerpVectors(inputs[0], inputs[1], inputs[2]);", + "});", + "NodeTypes['lerp'] = lerpNode;", + "const normalD = (x) => {", + " return (1 / Math.sqrt(2 * Math.PI)) * Math.exp(x * x * -0.5);", + "};", + "const normalDistributionNode = new NodeType('normDistrib');", + "normalDistributionNode.addSignature([NodeValueType.Number], [NodeValueType.Number], (context, data, inputs, outputs) => {", + " outputs[0] = normalD(inputs[0]);", + "});", + "NodeTypes['normDistrib'] = normalDistributionNode;", + "const normcdfNode = new NodeType('normcdf');", + "normcdfNode.addSignature([NodeValueType.Number], [NodeValueType.Number], (context, data, inputs, outputs) => {", + " let x = inputs[0];", + " const a1 = 0.254829592;", + " const a2 = -0.284496736;", + " const a3 = 1.421413741;", + " const a4 = -1.453152027;", + " const a5 = 1.061405429;", + " const p = 0.3275911;", + " let sign = 1;", + " if (x < 0)", + " sign = -1;", + " x = Math.abs(x) / Math.sqrt(2.0);", + " const t = 1.0 / (1.0 + p * x);", + " const y = 1.0 - ((((a5 * t + a4) * t + a3) * t + a2) * t + a1) * t * Math.exp(-x * x);", + " outputs[0] = 0.5 * (1.0 + sign * y);", + "});", + "NodeTypes['normcdf'] = normcdfNode;", + "const normcdfInvNode = new NodeType('normcdfInv');", + "const rationalApproximation = (t) => {", + " const c = [2.515517, 0.802853, 0.010328];", + " const d = [1.432788, 0.189269, 0.001308];", + " return t - ((c[2] * t + c[1]) * t + c[0]) / (((d[2] * t + d[1]) * t + d[0]) * t + 1.0);", + "};", + "const normcdfInv = (p) => {", + " if (p < 0.5) {", + " return -rationalApproximation(Math.sqrt(-2.0 * Math.log(p)));", + " }", + " else {", + " return rationalApproximation(Math.sqrt(-2.0 * Math.log(1 - p)));", + " }", + "};", + "normcdfInvNode.addSignature([NodeValueType.Number], [NodeValueType.Number], (context, data, inputs, outputs) => {", + " outputs[0] = normcdfInv(inputs[0]);", + "});", + "NodeTypes['normcdfInv'] = normcdfInvNode;", + "const clampNode = new NodeType('clamp');", + "clampNode.addSignature([NodeValueType.Number, NodeValueType.Number, NodeValueType.Number], [NodeValueType.Number], (context, data, inputs, outputs) => {", + " outputs[0] = Math.max(Math.min(inputs[0], inputs[2]), inputs[1]);", + "});", + "NodeTypes['clamp'] = clampNode;", + "const smoothstepNode = new NodeType('smoothstep');", + "smoothstepNode.addSignature([NodeValueType.Number, NodeValueType.Number, NodeValueType.Number], [NodeValueType.Number], (context, data, inputs, outputs) => {", + " const x = Math.max(Math.min(inputs[0], inputs[2]), inputs[1]);", + " outputs[0] = x * x * (3 - 2 * x);", + "});", + "NodeTypes['smoothstep'] = smoothstepNode;", + "const randomNode = new NodeType('random');", + "randomNode.addSignature([NodeValueType.Number, NodeValueType.Number], [NodeValueType.Number], (context, data, inputs, outputs) => {", + " outputs[0] = Math.random() * (inputs[1] - inputs[0]) + inputs[0];", + "});", + "randomNode.addSignature([NodeValueType.Vec2, NodeValueType.Vec2], [NodeValueType.Vec2], (context, data, inputs, outputs) => {", + " let random = Math.random();", + " outputs[0].lerpVectors(inputs[0], inputs[1], random);", + "});", + "randomNode.addSignature([NodeValueType.Vec3, NodeValueType.Vec3], [NodeValueType.Vec3], (context, data, inputs, outputs) => {", + " let random = Math.random();", + " outputs[0].lerpVectors(inputs[0], inputs[1], random);", + "});", + "randomNode.addSignature([NodeValueType.Vec4, NodeValueType.Vec4], [NodeValueType.Vec4], (context, data, inputs, outputs) => {", + " let random = Math.random();", + " outputs[0].lerpVectors(inputs[0], inputs[1], random);", + "});", + "NodeTypes['random'] = randomNode;", + "const vrandNode = new NodeType('vrand');", + "vrandNode.addSignature([], [NodeValueType.Vec2], (context, data, inputs, outputs) => {", + " let x = normcdfInv(Math.random());", + " let y = normcdfInv(Math.random());", + " const mag = Math.sqrt(x * x + y * y);", + " outputs[0].set(x / mag, y / mag);", + "});", + "vrandNode.addSignature([], [NodeValueType.Vec3], (context, data, inputs, outputs) => {", + " let x = normcdfInv(Math.random());", + " let y = normcdfInv(Math.random());", + " let z = normcdfInv(Math.random());", + " const mag = Math.sqrt(x * x + y * y + z * z);", + " outputs[0].set(x / mag, y / mag, z / mag);", + "});", + "vrandNode.addSignature([], [NodeValueType.Vec4], (context, data, inputs, outputs) => {", + " let x = normcdfInv(Math.random());", + " let y = normcdfInv(Math.random());", + " let z = normcdfInv(Math.random());", + " let w = normcdfInv(Math.random());", + " const mag = Math.sqrt(x * x + y * y + z * z + w * w);", + " outputs[0].set(x / mag, y / mag, z / mag, w / mag);", + "});", + "NodeTypes['vrand'] = vrandNode;", + "const bsdfNode = new NodeType('bsdf');", + "bsdfNode.addSignature([NodeValueType.Vec3, NodeValueType.Vec3, NodeValueType.Vec3, NodeValueType.Number], [], (context, data, inputs, outputs) => { });", + "NodeTypes['bsdf'] = bsdfNode;", + "const OutputNodeTypeNames = new Set(['output', 'particleProperty', 'graphProperty', 'emit']);", + "", + "class NodeGraph {", + " constructor(name) {", + " this.inputNodes = [];", + " this.outputNodes = [];", + " this.allNodes = new Map();", + " this.wires = [];", + " this.compiled = false;", + " this.nodesInOrder = [];", + " this.version = '1.0';", + " this.uuid = THREE.MathUtils.generateUUID();", + " this.name = name;", + " this.revision = 0;", + " }", + " addWire(wire) {", + " this.wires.push(wire);", + " this.revision++;", + " }", + " addNode(node) {", + " this.allNodes.set(node.id, node);", + " if (node.type === NodeTypes['input']) {", + " this.inputNodes.push(node);", + " }", + " else if (OutputNodeTypeNames.has(node.type.name)) {", + " this.outputNodes.push(node);", + " }", + " this.revision++;", + " }", + " getNode(id) {", + " return this.allNodes.get(id);", + " }", + " deleteNode(node) {", + " this.allNodes.delete(node.id);", + " this.revision++;", + " }", + " deleteWire(wire) {", + " let index = wire.input.outputs[wire.inputIndex].indexOf(wire);", + " if (index !== -1) {", + " wire.input.outputs[wire.inputIndex].splice(index, 1);", + " }", + " wire.output.inputs[wire.outputIndex] = undefined;", + " index = this.wires.indexOf(wire);", + " if (index != -1) {", + " this.wires[index] = this.wires[this.wires.length - 1];", + " this.wires.pop();", + " }", + " this.revision++;", + " }", + " toJSON() {", + " throw new Error('not implemented');", + " }", + " clone() {", + " throw new Error('not implemented');", + " }", + "}", + "", + "new THREE.Vector3(0, 0, 1);", + "const tempQ = new THREE.Quaternion();", + "const tempV = new THREE.Vector3();", + "const tempV2 = new THREE.Vector3();", + "const PREWARM_FPS = 60;", + "const DEFAULT_GEOMETRY = new THREE.PlaneGeometry(1, 1, 1, 1);", + "class NodeVFX {", + " set time(time) {", + " this.emissionState.time = time;", + " }", + " get time() {", + " return this.emissionState.time;", + " }", + " get layers() {", + " return this.rendererSettings.layers;", + " }", + " get texture() {", + " return this.rendererSettings.material.map;", + " }", + " set texture(texture) {", + " this.rendererSettings.material.map = texture;", + " this.neededToUpdateRender = true;", + " }", + " get material() {", + " return this.rendererSettings.material;", + " }", + " set material(material) {", + " this.rendererSettings.material = material;", + " this.neededToUpdateRender = true;", + " }", + " get instancingGeometry() {", + " return this.rendererSettings.instancingGeometry;", + " }", + " set instancingGeometry(geometry) {", + " this.restart();", + " this.particles.length = 0;", + " this.rendererSettings.instancingGeometry = geometry;", + " this.neededToUpdateRender = true;", + " }", + " get renderMode() {", + " return this.rendererSettings.renderMode;", + " }", + " set renderMode(renderMode) {", + " if ((this.rendererSettings.renderMode != RenderMode.Trail && renderMode === RenderMode.Trail) ||", + " (this.rendererSettings.renderMode == RenderMode.Trail && renderMode !== RenderMode.Trail)) {", + " this.restart();", + " this.particles.length = 0;", + " }", + " if (this.rendererSettings.renderMode !== renderMode) {", + " switch (renderMode) {", + " case RenderMode.Trail:", + " this.rendererEmitterSettings = {", + " startLength: 30,", + " followLocalOrigin: false,", + " };", + " break;", + " case RenderMode.Mesh:", + " this.rendererEmitterSettings = {", + " geometry: new THREE.PlaneGeometry(1, 1),", + " };", + " break;", + " case RenderMode.BillBoard:", + " case RenderMode.VerticalBillBoard:", + " case RenderMode.HorizontalBillBoard:", + " case RenderMode.StretchedBillBoard:", + " this.rendererEmitterSettings = {};", + " break;", + " }", + " }", + " this.rendererSettings.renderMode = renderMode;", + " this.neededToUpdateRender = true;", + " }", + " get renderOrder() {", + " return this.rendererSettings.renderOrder;", + " }", + " set renderOrder(renderOrder) {", + " this.rendererSettings.renderOrder = renderOrder;", + " this.neededToUpdateRender = true;", + " }", + " get blending() {", + " return this.rendererSettings.material.blending;", + " }", + " set blending(blending) {", + " this.rendererSettings.material.blending = blending;", + " this.neededToUpdateRender = true;", + " }", + " constructor(parameters) {", + " var _a, _b, _c, _d, _e, _f, _g;", + " this.temp = new THREE.Vector3();", + " this.travelDistance = 0;", + " this.normalMatrix = new THREE.Matrix3();", + " this.speedFactor = 0;", + " this.autoDestroy = parameters.autoDestroy === undefined ? false : parameters.autoDestroy;", + " this.duration = (_a = parameters.duration) !== null && _a !== void 0 ? _a : 1;", + " this.looping = parameters.looping === undefined ? true : parameters.looping;", + " this.prewarm = parameters.prewarm === undefined ? false : parameters.prewarm;", + " this.worldSpace = (_b = parameters.worldSpace) !== null && _b !== void 0 ? _b : false;", + " this.rendererEmitterSettings = (_c = parameters.rendererEmitterSettings) !== null && _c !== void 0 ? _c : {};", + " this.emissionGraph = parameters.emissionGraph;", + " this.updateGraph = parameters.updateGraph;", + " this.interpreter = new Interpreter();", + " this.rendererSettings = {", + " instancingGeometry: (_d = parameters.instancingGeometry) !== null && _d !== void 0 ? _d : DEFAULT_GEOMETRY,", + " renderMode: (_e = parameters.renderMode) !== null && _e !== void 0 ? _e : RenderMode.BillBoard,", + " renderOrder: (_f = parameters.renderOrder) !== null && _f !== void 0 ? _f : 0,", + " material: parameters.material,", + " layers: (_g = parameters.layers) !== null && _g !== void 0 ? _g : new THREE.Layers(),", + " uTileCount: 1,", + " vTileCount: 1,", + " };", + " this.neededToUpdateRender = true;", + " this.particles = new Array();", + " this.emitter = new ParticleEmitter(this);", + " this.paused = false;", + " this.particleNum = 0;", + " this.emissionState = {", + " time: 0,", + " };", + " this.emitEnded = false;", + " this.markForDestroy = false;", + " this.prewarmed = false;", + " }", + " pause() {", + " this.paused = true;", + " }", + " play() {", + " this.paused = false;", + " }", + " spawn(emissionState, matrix) {", + " tempQ.setFromRotationMatrix(matrix);", + " const translation = tempV;", + " const quaternion = tempQ;", + " const scale = tempV2;", + " matrix.decompose(translation, quaternion, scale);", + " this.particleNum++;", + " while (this.particles.length < this.particleNum) {", + " this.particles.push(new NodeParticle());", + " }", + " const particle = this.particles[this.particleNum - 1];", + " particle.reset();", + " this.interpreter.run(this.updateGraph, { particle: particle, emissionState: this.emissionState });", + " if (this.rendererSettings.renderMode === RenderMode.Trail &&", + " this.rendererEmitterSettings.followLocalOrigin) {", + " const trail = particle;", + " trail.localPosition = new THREE.Vector3().copy(trail.position);", + " }", + " if (this.worldSpace) {", + " particle.position.applyMatrix4(matrix);", + " particle.size *= (Math.abs(scale.x) + Math.abs(scale.y) + Math.abs(scale.z)) / 3;", + " particle.velocity.multiply(scale).applyMatrix3(this.normalMatrix);", + " if (particle.rotation && particle.rotation instanceof THREE.Quaternion) {", + " particle.rotation.multiplyQuaternions(tempQ, particle.rotation);", + " }", + " }", + " }", + " endEmit() {", + " this.emitEnded = true;", + " if (this.autoDestroy) {", + " this.markForDestroy = true;", + " }", + " }", + " dispose() {", + " if (this._renderer)", + " this._renderer.deleteSystem(this);", + " this.emitter.dispose();", + " if (this.emitter.parent)", + " this.emitter.parent.remove(this.emitter);", + " }", + " restart() {", + " this.paused = false;", + " this.particleNum = 0;", + " this.emissionState.time = 0;", + " this.emitEnded = false;", + " this.markForDestroy = false;", + " this.prewarmed = false;", + " }", + " update(delta) {", + " if (this.paused)", + " return;", + " let currentParent = this.emitter;", + " while (currentParent.parent) {", + " currentParent = currentParent.parent;", + " }", + " if (currentParent.type !== 'Scene') {", + " this.dispose();", + " return;", + " }", + " if (this.emitEnded && this.particleNum === 0) {", + " if (this.markForDestroy && this.emitter.parent)", + " this.dispose();", + " return;", + " }", + " if (this.looping && this.prewarm && !this.prewarmed) {", + " this.prewarmed = true;", + " for (let i = 0; i < this.duration * PREWARM_FPS; i++) {", + " this.update(1.0 / PREWARM_FPS);", + " }", + " }", + " if (delta > 0.1) {", + " delta = 0.1;", + " }", + " if (this.neededToUpdateRender) {", + " if (this._renderer)", + " this._renderer.updateSystem(this);", + " this.neededToUpdateRender = false;", + " }", + " this.emit(delta, this.emissionState, this.emitter.matrixWorld);", + " const context = { particle: undefined, emissionState: this.emissionState, delta };", + " for (let i = 0; i < this.particleNum; i++) {", + " context.particle = this.particles[i];", + " this.interpreter.run(this.updateGraph, context);", + " }", + " for (let i = 0; i < this.particleNum; i++) {", + " if (this.rendererEmitterSettings.followLocalOrigin &&", + " this.particles[i].localPosition) {", + " this.particles[i].position.copy(this.particles[i].localPosition);", + " if (this.particles[i].parentMatrix) {", + " this.particles[i].position.applyMatrix4(this.particles[i].parentMatrix);", + " }", + " else {", + " this.particles[i].position.applyMatrix4(this.emitter.matrixWorld);", + " }", + " }", + " else {", + " this.particles[i].position.addScaledVector(this.particles[i].velocity, delta);", + " }", + " this.particles[i].age += delta;", + " }", + " if (this.rendererSettings.renderMode === RenderMode.Trail) {", + " for (let i = 0; i < this.particleNum; i++) {", + " const particle = this.particles[i];", + " particle.update();", + " }", + " }", + " for (let i = 0; i < this.particleNum; i++) {", + " const particle = this.particles[i];", + " if (particle.died && (!(particle instanceof TrailParticle) || particle.previous.length === 0)) {", + " this.particles[i] = this.particles[this.particleNum - 1];", + " this.particles[this.particleNum - 1] = particle;", + " this.particleNum--;", + " i--;", + " }", + " }", + " }", + " emit(delta, emissionState, emitterMatrix) {", + " if (emissionState.time > this.duration) {", + " if (this.looping) {", + " emissionState.time -= this.duration;", + " }", + " else {", + " if (!this.emitEnded) {", + " this.endEmit();", + " }", + " }", + " }", + " this.normalMatrix.getNormalMatrix(emitterMatrix);", + " const context = {", + " signal: () => {", + " this.spawn(emissionState, emitterMatrix);", + " },", + " emissionState,", + " delta,", + " };", + " if (!this.emitEnded) {", + " this.interpreter.run(this.emissionGraph, context);", + " }", + " if (this.previousWorldPos === undefined)", + " this.previousWorldPos = new THREE.Vector3();", + " this.emitter.getWorldPosition(this.previousWorldPos);", + " emissionState.time += delta;", + " }", + " toJSON(meta, options = {}) {", + " return {};", + " }", + " getRendererSettings() {", + " return this.rendererSettings;", + " }", + " clone() {", + " return this;", + " }", + "}", + "", + "gdjs.__particleEmmiter3DExtension = {", + " ParticleEmitter3DRenderer,", + " ParticleEmitterAdapter,", + " ParticleEmitterHelper,", + "", + " ApplyCollision,", + " ApplyForce,", + " ApplySequences,", + " AxisAngleGenerator,", + " BatchedParticleRenderer,", + " BatchedRenderer,", + " BehaviorFromJSON,", + " BehaviorTypes,", + " Bezier,", + " ChangeEmitDirection,", + " ColorGeneratorFromJSON,", + " ColorOverLife,", + " ColorRange,", + " ConeEmitter,", + " ConstantColor,", + " ConstantValue,", + " DonutEmitter,", + " EmitSubParticleSystem,", + " EmitterFromJSON,", + " EmitterMode,", + " EmitterShapes,", + " EulerGenerator,", + " ForceOverLife,", + " FrameOverLife,", + " GeneratorFromJSON,", + " Gradient,", + " GraphNodeType,", + " GravityForce,", + " GridEmitter,", + " Interpreter,", + " IntervalValue,", + " MeshSurfaceEmitter,", + " Node,", + " NodeGraph,", + " NodeType,", + " NodeTypes,", + " NodeValueType,", + " Noise,", + " OrbitOverLife,", + " ParticleEmitter,", + " ParticleSystem,", + " PiecewiseBezier,", + " PiecewiseFunction,", + " Plugins,", + " PointEmitter,", + " QuarksLoader,", + " RandomColor,", + " RandomColorBetweenGradient,", + " RandomQuatGenerator,", + " RecordState,", + " RenderMode,", + " Rotation3DOverLife,", + " RotationGeneratorFromJSON,", + " RotationOverLife,", + " SequencerFromJSON,", + " SizeOverLife,", + " SpeedOverLife,", + " SphereEmitter,", + " SpriteBatch,", + " SpriteParticle,", + " SubParticleEmitMode,", + " TextureSequencer,", + " TrailBatch,", + " TrailParticle,", + " TurbulenceField,", + " VFXBatch,", + " ValueGeneratorFromJSON,", + " WidthOverLength,", + " Wire,", + " genDefaultForNodeValueType,", + " getPhysicsResolver,", + " loadPlugin,", + " setPhysicsResolver,", + " unloadPlugin,", + "}" + ], + "parameterObjects": "", + "useStrict": true, + "eventsSheetExpanded": true + } + ], + "parameters": [], + "objectGroups": [] + }, + { + "fullName": "", + "functionType": "Action", + "name": "onScenePreEvents", + "sentence": "", + "events": [ + { + "type": "BuiltinCommonInstructions::JsCode", + "inlineCode": [ + "\r", + "// See doStepPostEvents\r", + "runtimeScene.__particleEmmiter3DExtension = runtimeScene.__particleEmmiter3DExtension || {};\r", + "runtimeScene.__particleEmmiter3DExtension.emittersStepped = 0;" + ], + "parameterObjects": "", + "useStrict": true, + "eventsSheetExpanded": true + } + ], + "parameters": [], + "objectGroups": [] + } + ], + "eventsFunctionsFolderStructure": { + "folderName": "__ROOT", + "children": [ + { + "functionName": "DefineHelperClasses" + }, + { + "functionName": "onScenePreEvents" + } + ] + }, + "eventsBasedBehaviors": [], + "eventsBasedObjects": [ + { + "areaMaxX": 64, + "areaMaxY": 64, + "areaMaxZ": 64, + "areaMinX": 0, + "areaMinY": 0, + "areaMinZ": 0, + "assetStoreTag": "3d particles", + "defaultName": "ParticleEmitter", + "description": "Display a large number of particles to create visual effects.", + "fullName": "3D particle emitter", + "helpPath": "", + "iconUrl": "", + "is3D": true, + "isUsingLegacyInstancesRenderer": true, + "name": "ParticleEmitter3D", + "previewIconUrl": "", + "objects": [ + { + "adaptCollisionMaskAutomatically": true, + "assetStoreId": "", + "name": "Particle", + "type": "Sprite", + "updateIfNotVisible": false, + "variables": [], + "effects": [], + "behaviors": [], + "animations": [ + { + "name": "Image", + "useMultipleDirections": false, + "directions": [ + { + "looping": false, + "timeBetweenFrames": 0.08, + "sprites": [] + } + ] + } + ] + } + ], + "objectsFolderStructure": { + "folderName": "__ROOT", + "children": [ + { + "objectName": "Particle" + } + ] + }, + "objectsGroups": [], + "layers": [ + { + "ambientLightColorB": 200, + "ambientLightColorG": 200, + "ambientLightColorR": 200, + "camera2DPlaneMaxDrawingDistance": 5000, + "camera3DFarPlaneDistance": 10000, + "camera3DFieldOfView": 45, + "camera3DNearPlaneDistance": 3, + "cameraType": "", + "followBaseLayerCamera": false, + "isLightingLayer": false, + "isLocked": false, + "name": "", + "renderingType": "", + "visibility": true, + "cameras": [ + { + "defaultSize": true, + "defaultViewport": true, + "height": 0, + "viewportBottom": 1, + "viewportLeft": 0, + "viewportRight": 1, + "viewportTop": 0, + "width": 0 + } + ], + "effects": [] + } + ], + "instances": [], + "editionSettings": [], + "eventsFunctions": [ + { + "fullName": "", + "functionType": "Action", + "name": "onCreated", + "sentence": "", + "events": [ + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [], + "actions": [ + { + "type": { + "value": "ParticleEmitter3D::DefineHelperClasses" + }, + "parameters": [ + "", + "" + ] + } + ] + }, + { + "type": "BuiltinCommonInstructions::JsCode", + "inlineCode": [ + "const {", + " ParticleEmitterAdapter,", + " ParticleEmitter3DRenderer,", + " ParticleEmitterHelper,", + "", + " ParticleSystem,", + " IntervalValue,", + " ConstantValue,", + " ConstantColor,", + " ColorOverLife,", + " SizeOverLife,", + " ApplyForce,", + " WidthOverLength,", + " Gradient,", + " PiecewiseBezier,", + " Bezier,", + " PointEmitter,", + " ConeEmitter,", + " RenderMode", + "} = gdjs.__particleEmmiter3DExtension;", + "", + "/** @type {gdjs.CustomRuntimeObject} */", + "const object = objects[0];", + "// Force the size of the object because it doesn't contain any instance.", + "object._innerArea = {", + " min: [-16, -16, -16],", + " max: [16, 16, 16],", + " };", + "const hitbox = new gdjs.Polygon();", + "hitbox.vertices = [[-16, -16], [16, -16], [16, 16], [-16, 16]];", + "object._untransformedHitBoxes = [hitbox];", + "object._unrotatedAABB.min[0] = -16;", + "object._unrotatedAABB.min[1] = -16;", + "object._unrotatedAABB.max[0] = 16;", + "object._unrotatedAABB.max[1] = 16;", + "object._updateUntransformedHitBoxes = () => {};", + "", + "// Here runtimeScene is the gdjs.CustomRuntimeObjectInstanceContainer inside the custom object.", + "const gameScene = object.getRuntimeScene();", + "const game = runtimeScene.getGame();", + "", + "const texture = game.getImageManager().getThreeTexture('');", + "", + "// Set the blending here because changes are not applied after the emitter creation.", + "const blendingString = object._getBlending();", + "const blending =", + " blendingString === \"Additive\" ? THREE.AdditiveBlending :", + " blendingString === \"Normal\" ? THREE.NormalBlending :", + " blendingString === \"Subtractive\" ? THREE.SubtractiveBlending :", + " blendingString === \"Multiply\" ? THREE.MultiplyBlending :", + " blendingString === \"None\" ? THREE.NoBlending :", + " THREE.AdditiveBlending;", + "", + "", + "// Build a configuration with the right kind of objects.", + "// These values are not important as they are overrided by the object properties values.", + "const muzzle = {", + " duration: 10,", + " looping: false,", + " startLife: new IntervalValue(1, 2),", + " startSpeed: new IntervalValue(100, 200),", + " startSize: new IntervalValue(20, 50),", + " startColor: new ConstantColor(new THREE.Vector4(1, 1, 1, 1)),", + " worldSpace: true,", + "", + " maxParticle: 5,", + " emissionOverTime: new ConstantValue(50),", + " emissionBursts: [{", + " time: 0,", + " count: new ConstantValue(1),", + " cycle: 1,", + " interval: 0.01,", + " probability: 1,", + " }],", + "", + " shape: new PointEmitter(),", + " material: new THREE.MeshBasicMaterial({", + " map: texture,", + " blending: blending,", + " transparent: true,", + " side: THREE.DoubleSide", + " }),", + " startTileIndex: 0,", + " uTileCount: 1,", + " vTileCount: 1,", + " renderOrder: 2,", + " renderMode: RenderMode.BillBoard", + "};", + "const particleSystem = new ParticleSystem(muzzle);", + "", + "const colorOverLife = new ColorOverLife(", + " new Gradient(", + " [", + " [new THREE.Vector3(1, 0, 0), 0],", + " [new THREE.Vector3(1, 0, 0), 1],", + " ],", + " [", + " [1, 0],", + " [1, 1],", + " ]", + " ));", + "particleSystem.addBehavior(colorOverLife);", + "const sizeOverLife = new SizeOverLife(new PiecewiseBezier([[new Bezier(1, 2 / 3, 1 / 3, 0), 0]]));", + "particleSystem.addBehavior(sizeOverLife);", + "const applyForce = new ApplyForce(new THREE.Vector3(0, 0, -1), new ConstantValue(0));", + "particleSystem.addBehavior(applyForce);", + "const widthOverLength = new WidthOverLength(new PiecewiseBezier([[new Bezier(1, 2 / 3, 1 / 3, 0), 0]]));", + "particleSystem.addBehavior(widthOverLength);", + "", + "particleSystem.emitter.name = object.getName() + object.getNameId();", + "", + "object.__particleEmitterAdapter = new ParticleEmitterAdapter(particleSystem, colorOverLife, sizeOverLife, applyForce, widthOverLength);", + "object.__particleSystem = particleSystem;", + "", + "// This is a hack that may break in future releases.", + "// Replace the group that would hold children objects by the emmiter.", + "const layer = object.getInstanceContainer().getLayer(object.getLayer());", + "const group = object.getRenderer()._threeGroup;", + "layer.getRenderer().remove3DRendererObject(group);", + "particleSystem.emitter.position.copy(group.position);", + "particleSystem.emitter.rotation.order = 'ZYX';", + "particleSystem.emitter.rotation.copy(group.rotation);", + "", + "const particleEmitter3DRenderer = new ParticleEmitter3DRenderer(object, object._instanceContainer, object.getInstanceContainer());", + "object._renderer = particleEmitter3DRenderer;", + "if (game.isInGameEdition && game.isInGameEdition()) {", + " const particleEmitterHelper = new ParticleEmitterHelper(object);", + " particleEmitter3DRenderer._threeGroup = particleEmitterHelper;", + " layer.getRenderer().add3DRendererObject(particleEmitterHelper);", + "}", + "else {", + " particleEmitter3DRenderer._threeGroup = particleSystem.emitter;", + " layer.getRenderer().add3DRendererObject(particleSystem.emitter);", + "}", + "", + "particleSystem.emitter.updateMatrixWorld(true);", + "", + "", + "// See doStepPostEvents", + "gameScene.__particleEmmiter3DExtension = gameScene.__particleEmmiter3DExtension || {};", + "gameScene.__particleEmmiter3DExtension.emittersCount = gameScene.__particleEmmiter3DExtension.emittersCount || 0;", + "gameScene.__particleEmmiter3DExtension.emittersCount++;", + "", + "" + ], + "parameterObjects": "Object", + "useStrict": true, + "eventsSheetExpanded": true + }, + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [], + "actions": [ + { + "type": { + "value": "ParticleEmitter3D::ParticleEmitter3D::UpdateFromProperties" + }, + "parameters": [ + "Object", + "" + ] + }, + { + "type": { + "value": "ParticleEmitter3D::ParticleEmitter3D::SetRotationCenter" + }, + "parameters": [ + "Object", + "0", + "0", + "0" + ] + } + ] + } + ], + "parameters": [ + { + "description": "Object", + "name": "Object", + "supplementaryInformation": "ParticleEmitter3D::ParticleEmitter3D", + "type": "object" + } + ], + "objectGroups": [] + }, + { + "fullName": "", + "functionType": "Action", + "name": "onDestroy", + "sentence": "", + "events": [ + { + "type": "BuiltinCommonInstructions::JsCode", + "inlineCode": [ + "/** @type {gdjs.CustomRuntimeObject} */", + "const object = objects[0];", + "// Here runtimeScene is the gdjs.CustomRuntimeObjectInstanceContainer inside the custom object.", + "const gameScene = object.getRuntimeScene();", + "", + "object.__particleSystem.dispose();", + "", + "// See doStepPostEvents", + "gameScene.__particleEmmiter3DExtension.emittersCount--;", + "", + "if (gameScene.__particleEmmiter3DExtension.emittersCount === 0) {", + " // Update batch system now because doStepPostEvents won't be called.", + " gameScene.__particleEmmiter3DExtension.layerNames = gameScene.__particleEmmiter3DExtension.layerNames || [];", + " const layerNames = gameScene.__particleEmmiter3DExtension.layerNames;", + " gameScene.getAllLayerNames(layerNames);", + " for (const layerName of layerNames) {", + " const layer = gameScene.getLayer(layerName);", + " if (layer.__particleEmmiter3DExtension) {", + " layer.__particleEmmiter3DExtension.batchSystem.update(layer.getElapsedTime() / 1000);", + " }", + " }", + "}" + ], + "parameterObjects": "Object", + "useStrict": true, + "eventsSheetExpanded": true + } + ], + "parameters": [ + { + "description": "Object", + "name": "Object", + "supplementaryInformation": "ParticleEmitter3D::ParticleEmitter3D", + "type": "object" + } + ], + "objectGroups": [] + }, + { + "fullName": "", + "functionType": "Action", + "name": "onHotReloading", + "sentence": "", + "events": [ + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [], + "actions": [ + { + "type": { + "value": "ParticleEmitter3D::ParticleEmitter3D::UpdateFromProperties" + }, + "parameters": [ + "Object", + "" + ] + } + ] + } + ], + "parameters": [ + { + "description": "Object", + "name": "Object", + "supplementaryInformation": "ParticleEmitter3D::ParticleEmitter3D", + "type": "object" + } + ], + "objectGroups": [] + }, + { + "description": "Update from properties.", + "fullName": "Update from properties", + "functionType": "Action", + "name": "UpdateFromProperties", + "private": true, + "sentence": "Update from properties of _PARAM0_", + "events": [ + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [], + "actions": [ + { + "type": { + "value": "ParticleEmitter3D::ParticleEmitter3D::SetZ" + }, + "parameters": [ + "Object", + "=", + "Object.Z()", + "" + ] + }, + { + "type": { + "value": "ParticleEmitter3D::ParticleEmitter3D::SetRotationX" + }, + "parameters": [ + "Object", + "=", + "Object.PropertyRotationX()", + "" + ] + }, + { + "type": { + "value": "ParticleEmitter3D::ParticleEmitter3D::SetRotationY" + }, + "parameters": [ + "Object", + "=", + "Object.PropertyRotationY()", + "" + ] + }, + { + "type": { + "value": "ParticleEmitter3D::ParticleEmitter3D::SetRenderMode" + }, + "parameters": [ + "Object", + "=", + "RenderMode", + "" + ] + }, + { + "type": { + "value": "ParticleEmitter3D::ParticleEmitter3D::SetTrailStartLengthMin" + }, + "parameters": [ + "Object", + "=", + "TrailStartLengthMin", + "" + ] + }, + { + "type": { + "value": "ParticleEmitter3D::ParticleEmitter3D::SetTrailStartLengthMax" + }, + "parameters": [ + "Object", + "=", + "TrailStartLengthMax", + "" + ] + }, + { + "type": { + "value": "ParticleEmitter3D::ParticleEmitter3D::SetTrailEndWidthRatio" + }, + "parameters": [ + "Object", + "=", + "TrailEndWidthRatio", + "" + ] + }, + { + "type": { + "value": "ParticleEmitter3D::ParticleEmitter3D::SetStartColor" + }, + "parameters": [ + "Object", + "=", + "Object.StartColor()", + "" + ] + }, + { + "type": { + "value": "ParticleEmitter3D::ParticleEmitter3D::SetEndColor" + }, + "parameters": [ + "Object", + "=", + "Object.EndColor()", + "" + ] + }, + { + "type": { + "value": "ParticleEmitter3D::ParticleEmitter3D::SetStartOpacity" + }, + "parameters": [ + "Object", + "=", + "Object.StartOpacity()", + "" + ] + }, + { + "type": { + "value": "ParticleEmitter3D::ParticleEmitter3D::SetEndOpacity" + }, + "parameters": [ + "Object", + "=", + "Object.EndOpacity()", + "" + ] + }, + { + "type": { + "value": "ParticleEmitter3D::ParticleEmitter3D::SetFlow" + }, + "parameters": [ + "Object", + "=", + "Object.Flow()", + "" + ] + }, + { + "type": { + "value": "ParticleEmitter3D::ParticleEmitter3D::SetStartSizeMin" + }, + "parameters": [ + "Object", + "=", + "Object.StartSizeMin()", + "" + ] + }, + { + "type": { + "value": "ParticleEmitter3D::ParticleEmitter3D::SetStartSizeMax" + }, + "parameters": [ + "Object", + "=", + "Object.StartSizeMax()", + "" + ] + }, + { + "type": { + "value": "ParticleEmitter3D::ParticleEmitter3D::SetEndScale" + }, + "parameters": [ + "Object", + "=", + "Object.EndScale()", + "" + ] + }, + { + "type": { + "value": "ParticleEmitter3D::ParticleEmitter3D::SetStartSpeedMin" + }, + "parameters": [ + "Object", + "=", + "Object.StartSpeedMin()", + "" + ] + }, + { + "type": { + "value": "ParticleEmitter3D::ParticleEmitter3D::SetStartSpeedMax" + }, + "parameters": [ + "Object", + "=", + "Object.StartSpeedMax()", + "" + ] + }, + { + "type": { + "value": "ParticleEmitter3D::ParticleEmitter3D::SetLifespanMin" + }, + "parameters": [ + "Object", + "=", + "Object.LifespanMin()", + "" + ] + }, + { + "type": { + "value": "ParticleEmitter3D::ParticleEmitter3D::SetLifespanMax" + }, + "parameters": [ + "Object", + "=", + "Object.LifespanMax()", + "" + ] + }, + { + "type": { + "value": "ParticleEmitter3D::ParticleEmitter3D::SetDuration" + }, + "parameters": [ + "Object", + "=", + "Object.Duration()", + "" + ] + }, + { + "type": { + "value": "ParticleEmitter3D::ParticleEmitter3D::SetSpayConeAngle" + }, + "parameters": [ + "Object", + "=", + "Object.SpayConeAngle()", + "" + ] + }, + { + "type": { + "value": "ParticleEmitter3D::ParticleEmitter3D::SetGravity" + }, + "parameters": [ + "Object", + "=", + "Object.Gravity()", + "" + ] + }, + { + "type": { + "value": "ParticleEmitter3D::ParticleEmitter3D::SetGravityTop" + }, + "parameters": [ + "Object", + "=", + "Object.GravityTop()", + "" + ] + }, + { + "type": { + "value": "ParticleEmitter3D::ParticleEmitter3D::UpdateImage" + }, + "parameters": [ + "Object", + "" + ] + } + ], + "events": [ + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [ + { + "type": { + "inverted": true, + "value": "BooleanVariable" + }, + "parameters": [ + "IsTrailFollowingLocalOrigin", + "True", + "" + ] + } + ], + "actions": [ + { + "type": { + "value": "ParticleEmitter3D::ParticleEmitter3D::SetIsTrailFollowingLocalOrigin" + }, + "parameters": [ + "Object", + "=", + "TrailStartLengthMax" + ] + } + ] + }, + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [ + { + "type": { + "value": "BooleanVariable" + }, + "parameters": [ + "IsTrailFollowingLocalOrigin", + "True", + "" + ] + } + ], + "actions": [ + { + "type": { + "value": "ParticleEmitter3D::ParticleEmitter3D::SetIsTrailFollowingLocalOrigin" + }, + "parameters": [ + "Object", + "yes", + "TrailStartLengthMax" + ] + } + ] + }, + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [ + { + "type": { + "inverted": true, + "value": "BooleanVariable" + }, + "parameters": [ + "AreParticlesRelative", + "True", + "" + ] + } + ], + "actions": [ + { + "type": { + "value": "ParticleEmitter3D::ParticleEmitter3D::SetAreParticlesRelative" + }, + "parameters": [ + "Object", + "no", + "" + ] + } + ] + }, + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [ + { + "type": { + "value": "BooleanVariable" + }, + "parameters": [ + "AreParticlesRelative", + "True", + "" + ] + } + ], + "actions": [ + { + "type": { + "value": "ParticleEmitter3D::ParticleEmitter3D::SetAreParticlesRelative" + }, + "parameters": [ + "Object", + "yes", + "" + ] + } + ] + }, + { + "type": "BuiltinCommonInstructions::Comment", + "color": { + "b": 109, + "g": 230, + "r": 255, + "textB": 0, + "textG": 0, + "textR": 0 + }, + "comment": "TODO: Blending can't be changed." + }, + { + "disabled": true, + "type": "BuiltinCommonInstructions::Standard", + "conditions": [], + "actions": [ + { + "type": { + "value": "ParticleEmitter3D::ParticleEmitter3D::SetBlending" + }, + "parameters": [ + "Object", + "=", + "Object.Blending()", + "" + ] + } + ] + } + ] + }, + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [], + "actions": [ + { + "type": { + "value": "ParticleEmitter3D::ParticleEmitter3D::UpdateHelper" + }, + "parameters": [ + "Object", + "" + ] + } + ] + } + ], + "parameters": [ + { + "description": "Object", + "name": "Object", + "supplementaryInformation": "ParticleEmitter3D::ParticleEmitter3D", + "type": "object" + } + ], + "objectGroups": [] + }, + { + "fullName": "Update helper", + "functionType": "Action", + "name": "UpdateHelper", + "private": true, + "sentence": "Update graphical helper of _PARAM0_", + "events": [ + { + "type": "BuiltinCommonInstructions::JsCode", + "inlineCode": [ + "const game = runtimeScene.getGame();", + "if (game.isInGameEdition && game.isInGameEdition()) {", + " const particleEmitterHelper = objects[0].get3DRendererObject();", + " particleEmitterHelper.update();", + "}" + ], + "parameterObjects": "Object", + "useStrict": true, + "eventsSheetExpanded": false + } + ], + "parameters": [ + { + "description": "Object", + "name": "Object", + "supplementaryInformation": "ParticleEmitter3D::ParticleEmitter3D", + "type": "object" + } + ], + "objectGroups": [] + }, + { + "fullName": "Update particle image", + "functionType": "Action", + "name": "UpdateImage", + "private": true, + "sentence": "Update particle image of _PARAM0_", + "events": [ + { + "type": "BuiltinCommonInstructions::JsCode", + "inlineCode": [ + "/** @type {gdjs.CustomRuntimeObject3D} */\r", + "const object = objects[0];\r", + "/** @type {SpriteObjectDataType} */\r", + "const particleSpriteData = object._instanceContainer._objects.get(\"Particle\");\r", + "/** @type {gdjs.SpriteAnimationData} */\r", + "const animation = particleSpriteData.animations[0];\r", + "const animationFrame = animation ? animation.directions[0].sprites[0] : null;\r", + "const resourceName = animationFrame ? animationFrame.image : '';\r", + "object.__particleEmitterAdapter.setImage(resourceName);" + ], + "parameterObjects": "Object", + "useStrict": true, + "eventsSheetExpanded": true + } + ], + "parameters": [ + { + "description": "Object", + "name": "Object", + "supplementaryInformation": "ParticleEmitter3D::ParticleEmitter3D", + "type": "object" + } + ], + "objectGroups": [] + }, + { + "fullName": "", + "functionType": "Action", + "name": "doStepPostEvents", + "sentence": "", + "events": [ + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [ + { + "type": { + "value": "ParticleEmitter3D::ParticleEmitter3D::HasEnded" + }, + "parameters": [ + "Object", + "" + ] + } + ], + "actions": [ + { + "type": { + "value": "ParticleEmitter3D::ParticleEmitter3D::Delete" + }, + "parameters": [ + "Object", + "" + ] + } + ] + }, + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [ + { + "type": { + "inverted": true, + "value": "ParticleEmitter3D::ParticleEmitter3D::HasEnded" + }, + "parameters": [ + "Object", + "" + ] + } + ], + "actions": [ + { + "type": { + "value": "ParticleEmitter3D::ParticleEmitter3D::RegisterInLayer" + }, + "parameters": [ + "Object", + "" + ] + } + ], + "events": [ + { + "type": "BuiltinCommonInstructions::JsCode", + "inlineCode": [ + "/** @type {gdjs.CustomRuntimeObject3D} */", + "const object = objects[0];", + "// Here runtimeScene is the gdjs.CustomRuntimeObjectInstanceContainer inside the custom object.", + "const gameScene = object.getRuntimeScene();", + "", + "// Update batch system after having moved every emitter.", + "// See onScenePreEvents", + "gameScene.__particleEmmiter3DExtension.emittersStepped++;", + "if (gameScene.__particleEmmiter3DExtension.emittersStepped === gameScene.__particleEmmiter3DExtension.emittersCount) {", + " gameScene.__particleEmmiter3DExtension.layerNames = gameScene.__particleEmmiter3DExtension.layerNames || [];", + " const layerNames = gameScene.__particleEmmiter3DExtension.layerNames;", + " gameScene.getAllLayerNames(layerNames);", + " for (const layerName of layerNames) {", + " const layer = gameScene.getLayer(layerName);", + " if (layer.__particleEmmiter3DExtension) {", + " const timeDelta = layer.getElapsedTime() / 1000;", + " if (timeDelta > 0) {", + " layer.__particleEmmiter3DExtension.batchSystem.update(timeDelta);", + " }", + " }", + " }", + "}" + ], + "parameterObjects": "Object", + "useStrict": true, + "eventsSheetExpanded": true + } + ] + } + ], + "parameters": [ + { + "description": "Object", + "name": "Object", + "supplementaryInformation": "ParticleEmitter3D::ParticleEmitter3D", + "type": "object" + } + ], + "objectGroups": [] + }, + { + "fullName": "Register in layer", + "functionType": "Action", + "name": "RegisterInLayer", + "private": true, + "sentence": "Register _PARAM0_ in layer", + "events": [ + { + "type": "BuiltinCommonInstructions::JsCode", + "inlineCode": [ + "/** @type {gdjs.CustomRuntimeObject} */", + "const object = objects[0];", + "", + "// TODO Handle layer changes?", + "if (object.__emitterLayerName === undefined) {", + " // Here runtimeScene is the gdjs.CustomRuntimeObjectInstanceContainer inside the custom object.", + " const gameScene = object.getRuntimeScene();", + "", + " let rootObject = object;", + " while (rootObject.getInstanceContainer().getOwner) {", + " rootObject = rootObject.getInstanceContainer().getOwner();", + " }", + "", + " const layer = gameScene.getLayer(rootObject.getLayer());", + " layer.__particleEmmiter3DExtension = layer.__particleEmmiter3DExtension || {};", + " if (!layer.__particleEmmiter3DExtension.batchSystem) {", + " const batchSystem = new gdjs.__particleEmmiter3DExtension.BatchedRenderer();", + " batchSystem.scale.y *= -1;", + " const threeScene = layer.getRenderer().getThreeScene();", + " if (threeScene) {", + " threeScene.add(batchSystem);", + " }", + " layer.__particleEmmiter3DExtension.batchSystem = batchSystem;", + " }", + " layer.__particleEmmiter3DExtension.batchSystem.addSystem(object.__particleSystem);", + " object.__emitterLayerName = layer.getName();", + "}" + ], + "parameterObjects": "Object", + "useStrict": true, + "eventsSheetExpanded": true + } + ], + "parameters": [ + { + "description": "Object", + "name": "Object", + "supplementaryInformation": "ParticleEmitter3D::ParticleEmitter3D", + "type": "object" + } + ], + "objectGroups": [] + }, + { + "description": "Delete itself", + "fullName": "Delete itself", + "functionType": "Action", + "name": "Delete", + "private": true, + "sentence": "Delete _PARAM0_", + "events": [ + { + "type": "BuiltinCommonInstructions::JsCode", + "inlineCode": [ + "const object = objects[0];", + "", + "object.deleteFromScene(object.getParent());", + "" + ], + "parameterObjects": "Object", + "useStrict": true, + "eventsSheetExpanded": true + } + ], + "parameters": [ + { + "description": "Object", + "name": "Object", + "supplementaryInformation": "ParticleEmitter3D::ParticleEmitter3D", + "type": "object" + } + ], + "objectGroups": [] + }, + { + "description": "Check that emission has ended and no particle is alive anymore.", + "fullName": "Emission has ended", + "functionType": "Condition", + "name": "HasEnded", + "sentence": "Emission from _PARAM0_ has ended", + "events": [ + { + "type": "BuiltinCommonInstructions::JsCode", + "inlineCode": [ + "const object = objects[0];\r", + "\r", + "eventsFunctionContext.returnValue =\r", + " object._getShouldAutodestruct()\r", + " && object.__particleSystem.emitEnded\r", + " && object.__particleSystem.particleNum === 0;\r", + "" + ], + "parameterObjects": "Object", + "useStrict": true, + "eventsSheetExpanded": true + } + ], + "parameters": [ + { + "description": "Object", + "name": "Object", + "supplementaryInformation": "ParticleEmitter3D::ParticleEmitter3D", + "type": "object" + } + ], + "objectGroups": [] + }, + { + "description": "Restart particule emission from the beginning.", + "fullName": "Restart", + "functionType": "Action", + "name": "Restart", + "sentence": "Restart particule emission from _PARAM0_", + "events": [ + { + "type": "BuiltinCommonInstructions::JsCode", + "inlineCode": [ + "/** @type {gdjs.CustomRuntimeObject} */\r", + "const object = objects[0];\r", + "\r", + "object.__particleSystem.restart();\r", + "" + ], + "parameterObjects": "Object", + "useStrict": true, + "eventsSheetExpanded": false + } + ], + "parameters": [ + { + "description": "Object", + "name": "Object", + "supplementaryInformation": "ParticleEmitter3D::ParticleEmitter3D", + "type": "object" + } + ], + "objectGroups": [] + }, + { + "description": "the Z position of the emitter.", + "fullName": "Z elevaltion (deprecated)", + "functionType": "ExpressionAndCondition", + "group": "Position", + "name": "Z", + "private": true, + "sentence": "the Z position", + "events": [ + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [], + "actions": [ + { + "type": { + "value": "SetReturnNumber" + }, + "parameters": [ + "Object.PropertyZ()" + ] + } + ] + } + ], + "expressionType": { + "type": "expression" + }, + "parameters": [ + { + "description": "Object", + "name": "Object", + "supplementaryInformation": "ParticleEmitter3D::ParticleEmitter3D", + "type": "object" + } + ], + "objectGroups": [] + }, + { + "fullName": "", + "functionType": "ActionWithOperator", + "getterName": "Z", + "group": "Position", + "name": "SetZ", + "private": true, + "sentence": "", + "events": [ + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [], + "actions": [ + { + "type": { + "value": "SetNumberVariable" + }, + "parameters": [ + "Z", + "=", + "Value" + ] + } + ] + } + ], + "parameters": [ + { + "description": "Object", + "name": "Object", + "supplementaryInformation": "ParticleEmitter3D::ParticleEmitter3D", + "type": "object" + } + ], + "objectGroups": [] + }, + { + "description": "the rotation on X axis of the emitter.", + "fullName": "Rotation on X axis (deprecated)", + "functionType": "ExpressionAndCondition", + "group": "Angle", + "name": "RotationX", + "private": true, + "sentence": "the rotation on X axis", + "events": [ + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [], + "actions": [ + { + "type": { + "value": "SetReturnNumber" + }, + "parameters": [ + "Object.PropertyRotationX()" + ] + } + ] + } + ], + "expressionType": { + "type": "expression" + }, + "parameters": [ + { + "description": "Object", + "name": "Object", + "supplementaryInformation": "ParticleEmitter3D::ParticleEmitter3D", + "type": "object" + } + ], + "objectGroups": [] + }, + { + "fullName": "", + "functionType": "ActionWithOperator", + "getterName": "RotationX", + "group": "Angle", + "name": "SetRotationX", + "private": true, + "sentence": "", + "events": [ + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [], + "actions": [ + { + "type": { + "value": "SetNumberVariable" + }, + "parameters": [ + "RotationX", + "=", + "Value" + ] + } + ] + }, + { + "type": "BuiltinCommonInstructions::JsCode", + "inlineCode": [ + "const object = objects[0];\r", + "\r", + "object.__particleSystem.emitter.rotation.x = eventsFunctionContext.getArgument(\"Value\") * Math.PI / 180;" + ], + "parameterObjects": "Object", + "useStrict": true, + "eventsSheetExpanded": false + } + ], + "parameters": [ + { + "description": "Object", + "name": "Object", + "supplementaryInformation": "ParticleEmitter3D::ParticleEmitter3D", + "type": "object" + } + ], + "objectGroups": [] + }, + { + "description": "the rotation on Y axis of the emitter.", + "fullName": "Rotation on Y axis (deprecated)", + "functionType": "ExpressionAndCondition", + "group": "Angle", + "name": "RotationY", + "private": true, + "sentence": "the rotation on Y axis", + "events": [ + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [], + "actions": [ + { + "type": { + "value": "SetReturnNumber" + }, + "parameters": [ + "Object.PropertyRotationY()" + ] + } + ] + } + ], + "expressionType": { + "type": "expression" + }, + "parameters": [ + { + "description": "Object", + "name": "Object", + "supplementaryInformation": "ParticleEmitter3D::ParticleEmitter3D", + "type": "object" + } + ], + "objectGroups": [] + }, + { + "fullName": "", + "functionType": "ActionWithOperator", + "getterName": "RotationY", + "group": "Angle", + "name": "SetRotationY", + "private": true, + "sentence": "", + "events": [ + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [], + "actions": [ + { + "type": { + "value": "SetNumberVariable" + }, + "parameters": [ + "RotationY", + "=", + "Value" + ] + } + ] + }, + { + "type": "BuiltinCommonInstructions::JsCode", + "inlineCode": [ + "const object = objects[0];\r", + "\r", + "object.__particleSystem.emitter.rotation.y = (90 + eventsFunctionContext.getArgument(\"Value\")) * Math.PI / 180;" + ], + "parameterObjects": "Object", + "useStrict": true, + "eventsSheetExpanded": false + } + ], + "parameters": [ + { + "description": "Object", + "name": "Object", + "supplementaryInformation": "ParticleEmitter3D::ParticleEmitter3D", + "type": "object" + } + ], + "objectGroups": [] + }, + { + "description": "the start color of the object.", + "fullName": "Start color", + "functionType": "ExpressionAndCondition", + "group": "3D particle emitter color configuration", + "name": "StartColor", + "sentence": "the start color", + "events": [ + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [], + "actions": [ + { + "type": { + "value": "SetReturnString" + }, + "parameters": [ + "StartColor" + ] + } + ] + } + ], + "expressionType": { + "type": "color" + }, + "parameters": [ + { + "description": "Object", + "name": "Object", + "supplementaryInformation": "ParticleEmitter3D::ParticleEmitter3D", + "type": "object" + } + ], + "objectGroups": [] + }, + { + "fullName": "", + "functionType": "ActionWithOperator", + "getterName": "StartColor", + "group": "3D particle emitter color configuration", + "name": "SetStartColor", + "sentence": "", + "events": [ + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [], + "actions": [ + { + "type": { + "value": "SetStringVariable" + }, + "parameters": [ + "StartColor", + "=", + "Value" + ] + } + ] + }, + { + "type": "BuiltinCommonInstructions::JsCode", + "inlineCode": [ + "const object = objects[0];\r", + "const startColor = eventsFunctionContext.getArgument(\"Value\");\r", + "\r", + "object.__particleEmitterAdapter.setStartColor(startColor);" + ], + "parameterObjects": "Object", + "useStrict": true, + "eventsSheetExpanded": false + } + ], + "parameters": [ + { + "description": "Object", + "name": "Object", + "supplementaryInformation": "ParticleEmitter3D::ParticleEmitter3D", + "type": "object" + } + ], + "objectGroups": [] + }, + { + "description": "the end color of the object.", + "fullName": "End color", + "functionType": "ExpressionAndCondition", + "group": "3D particle emitter color configuration", + "name": "EndColor", + "sentence": "the end color", + "events": [ + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [], + "actions": [ + { + "type": { + "value": "SetReturnString" + }, + "parameters": [ + "EndColor" + ] + } + ] + } + ], + "expressionType": { + "type": "color" + }, + "parameters": [ + { + "description": "Object", + "name": "Object", + "supplementaryInformation": "ParticleEmitter3D::ParticleEmitter3D", + "type": "object" + } + ], + "objectGroups": [] + }, + { + "fullName": "", + "functionType": "ActionWithOperator", + "getterName": "EndColor", + "group": "3D particle emitter color configuration", + "name": "SetEndColor", + "sentence": "", + "events": [ + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [], + "actions": [ + { + "type": { + "value": "SetStringVariable" + }, + "parameters": [ + "EndColor", + "=", + "Value" + ] + } + ] + }, + { + "type": "BuiltinCommonInstructions::JsCode", + "inlineCode": [ + "const object = objects[0];\r", + "const endColor = eventsFunctionContext.getArgument(\"Value\");\r", + "\r", + "object.__particleEmitterAdapter.setEndColor(endColor);" + ], + "parameterObjects": "Object", + "useStrict": true, + "eventsSheetExpanded": false + } + ], + "parameters": [ + { + "description": "Object", + "name": "Object", + "supplementaryInformation": "ParticleEmitter3D::ParticleEmitter3D", + "type": "object" + } + ], + "objectGroups": [] + }, + { + "description": "the start opacity of the object.", + "fullName": "Start opacity", + "functionType": "ExpressionAndCondition", + "group": "3D particle emitter color configuration", + "name": "StartOpacity", + "sentence": "the start opacity", + "events": [ + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [], + "actions": [ + { + "type": { + "value": "SetReturnNumber" + }, + "parameters": [ + "StartOpacity" + ] + } + ] + } + ], + "expressionType": { + "type": "expression" + }, + "parameters": [ + { + "description": "Object", + "name": "Object", + "supplementaryInformation": "ParticleEmitter3D::ParticleEmitter3D", + "type": "object" + } + ], + "objectGroups": [] + }, + { + "fullName": "", + "functionType": "ActionWithOperator", + "getterName": "StartOpacity", + "group": "3D particle emitter color configuration", + "name": "SetStartOpacity", + "sentence": "", + "events": [ + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [], + "actions": [ + { + "type": { + "value": "SetNumberVariable" + }, + "parameters": [ + "StartOpacity", + "=", + "Value" + ] + } + ] + }, + { + "type": "BuiltinCommonInstructions::JsCode", + "inlineCode": [ + "const object = objects[0];\r", + "const startOpacity = eventsFunctionContext.getArgument(\"Value\");\r", + "\r", + "object.__particleEmitterAdapter.setStartOpacity(startOpacity);" + ], + "parameterObjects": "Object", + "useStrict": true, + "eventsSheetExpanded": false + } + ], + "parameters": [ + { + "description": "Object", + "name": "Object", + "supplementaryInformation": "ParticleEmitter3D::ParticleEmitter3D", + "type": "object" + } + ], + "objectGroups": [] + }, + { + "description": "the end opacity of the object.", + "fullName": "End opacity", + "functionType": "ExpressionAndCondition", + "group": "3D particle emitter color configuration", + "name": "EndOpacity", + "sentence": "the end opacity", + "events": [ + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [], + "actions": [ + { + "type": { + "value": "SetReturnNumber" + }, + "parameters": [ + "EndOpacity" + ] + } + ] + } + ], + "expressionType": { + "type": "expression" + }, + "parameters": [ + { + "description": "Object", + "name": "Object", + "supplementaryInformation": "ParticleEmitter3D::ParticleEmitter3D", + "type": "object" + } + ], + "objectGroups": [] + }, + { + "fullName": "", + "functionType": "ActionWithOperator", + "getterName": "EndOpacity", + "group": "3D particle emitter color configuration", + "name": "SetEndOpacity", + "sentence": "", + "events": [ + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [], + "actions": [ + { + "type": { + "value": "SetNumberVariable" + }, + "parameters": [ + "EndOpacity", + "=", + "Value" + ] + } + ] + }, + { + "type": "BuiltinCommonInstructions::JsCode", + "inlineCode": [ + "const object = objects[0];\r", + "const endOpacity = eventsFunctionContext.getArgument(\"Value\");\r", + "\r", + "object.__particleEmitterAdapter.setEndOpacity(endOpacity);" + ], + "parameterObjects": "Object", + "useStrict": true, + "eventsSheetExpanded": false + } + ], + "parameters": [ + { + "description": "Object", + "name": "Object", + "supplementaryInformation": "ParticleEmitter3D::ParticleEmitter3D", + "type": "object" + } + ], + "objectGroups": [] + }, + { + "description": "the flow of particles of the object (particles per second).", + "fullName": "Flow of particles", + "functionType": "ExpressionAndCondition", + "group": "3D particle emitter configuration", + "name": "Flow", + "sentence": "the flow of particles", + "events": [ + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [], + "actions": [ + { + "type": { + "value": "SetReturnNumber" + }, + "parameters": [ + "Flow" + ] + } + ] + } + ], + "expressionType": { + "type": "expression" + }, + "parameters": [ + { + "description": "Object", + "name": "Object", + "supplementaryInformation": "ParticleEmitter3D::ParticleEmitter3D", + "type": "object" + } + ], + "objectGroups": [] + }, + { + "fullName": "", + "functionType": "ActionWithOperator", + "getterName": "Flow", + "group": "3D particle emitter configuration", + "name": "SetFlow", + "sentence": "", + "events": [ + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [], + "actions": [ + { + "type": { + "value": "SetNumberVariable" + }, + "parameters": [ + "Flow", + "=", + "Value" + ] + } + ] + }, + { + "type": "BuiltinCommonInstructions::JsCode", + "inlineCode": [ + "const object = objects[0];\r", + "const flow = eventsFunctionContext.getArgument(\"Value\");\r", + "\r", + "object.__particleEmitterAdapter.setFlow(flow);\r", + "" + ], + "parameterObjects": "Object", + "useStrict": true, + "eventsSheetExpanded": false + } + ], + "parameters": [ + { + "description": "Object", + "name": "Object", + "supplementaryInformation": "ParticleEmitter3D::ParticleEmitter3D", + "type": "object" + } + ], + "objectGroups": [] + }, + { + "description": "the start min size of the object.", + "fullName": "Start min size", + "functionType": "ExpressionAndCondition", + "group": "3D particle emitter configuration", + "name": "StartSizeMin", + "sentence": "the start min size", + "events": [ + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [], + "actions": [ + { + "type": { + "value": "SetReturnNumber" + }, + "parameters": [ + "StartSizeMin" + ] + } + ] + } + ], + "expressionType": { + "type": "expression" + }, + "parameters": [ + { + "description": "Object", + "name": "Object", + "supplementaryInformation": "ParticleEmitter3D::ParticleEmitter3D", + "type": "object" + } + ], + "objectGroups": [] + }, + { + "fullName": "", + "functionType": "ActionWithOperator", + "getterName": "StartSizeMin", + "group": "3D particle emitter configuration", + "name": "SetStartSizeMin", + "sentence": "", + "events": [ + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [], + "actions": [ + { + "type": { + "value": "SetNumberVariable" + }, + "parameters": [ + "StartSizeMin", + "=", + "Value" + ] + } + ] + }, + { + "type": "BuiltinCommonInstructions::JsCode", + "inlineCode": [ + "const object = objects[0];\r", + "const startMinSize = eventsFunctionContext.getArgument(\"Value\");\r", + "\r", + "object.__particleEmitterAdapter.setStartMinSize(startMinSize);" + ], + "parameterObjects": "Object", + "useStrict": true, + "eventsSheetExpanded": false + } + ], + "parameters": [ + { + "description": "Object", + "name": "Object", + "supplementaryInformation": "ParticleEmitter3D::ParticleEmitter3D", + "type": "object" + } + ], + "objectGroups": [] + }, + { + "description": "the start max size of the object.", + "fullName": "Start max size", + "functionType": "ExpressionAndCondition", + "group": "3D particle emitter configuration", + "name": "StartSizeMax", + "sentence": "the start max size", + "events": [ + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [], + "actions": [ + { + "type": { + "value": "SetReturnNumber" + }, + "parameters": [ + "StartSizeMax" + ] + } + ] + } + ], + "expressionType": { + "type": "expression" + }, + "parameters": [ + { + "description": "Object", + "name": "Object", + "supplementaryInformation": "ParticleEmitter3D::ParticleEmitter3D", + "type": "object" + } + ], + "objectGroups": [] + }, + { + "fullName": "", + "functionType": "ActionWithOperator", + "getterName": "StartSizeMax", + "group": "3D particle emitter configuration", + "name": "SetStartSizeMax", + "sentence": "", + "events": [ + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [], + "actions": [ + { + "type": { + "value": "SetNumberVariable" + }, + "parameters": [ + "StartSizeMax", + "=", + "Value" + ] + } + ] + }, + { + "type": "BuiltinCommonInstructions::JsCode", + "inlineCode": [ + "const object = objects[0];\r", + "const startMaxSize = eventsFunctionContext.getArgument(\"Value\");\r", + "\r", + "object.__particleEmitterAdapter.setStartMaxSize(startMaxSize);\r", + "" + ], + "parameterObjects": "Object", + "useStrict": true, + "eventsSheetExpanded": false + } + ], + "parameters": [ + { + "description": "Object", + "name": "Object", + "supplementaryInformation": "ParticleEmitter3D::ParticleEmitter3D", + "type": "object" + } + ], + "objectGroups": [] + }, + { + "description": "the end scale of the object.", + "fullName": "End scale", + "functionType": "ExpressionAndCondition", + "group": "3D particle emitter configuration", + "name": "EndScale", + "sentence": "the end scale", + "events": [ + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [], + "actions": [ + { + "type": { + "value": "SetReturnNumber" + }, + "parameters": [ + "EndScale" + ] + } + ] + } + ], + "expressionType": { + "type": "expression" + }, + "parameters": [ + { + "description": "Object", + "name": "Object", + "supplementaryInformation": "ParticleEmitter3D::ParticleEmitter3D", + "type": "object" + } + ], + "objectGroups": [] + }, + { + "fullName": "", + "functionType": "ActionWithOperator", + "getterName": "EndScale", + "group": "3D particle emitter configuration", + "name": "SetEndScale", + "sentence": "", + "events": [ + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [], + "actions": [ + { + "type": { + "value": "SetNumberVariable" + }, + "parameters": [ + "EndScale", + "=", + "Value" + ] + } + ] + }, + { + "type": "BuiltinCommonInstructions::JsCode", + "inlineCode": [ + "const object = objects[0];\r", + "const endScale = eventsFunctionContext.getArgument(\"Value\");\r", + "\r", + "object.__particleEmitterAdapter.setEndScale(endScale);\r", + "" + ], + "parameterObjects": "Object", + "useStrict": true, + "eventsSheetExpanded": false + } + ], + "parameters": [ + { + "description": "Object", + "name": "Object", + "supplementaryInformation": "ParticleEmitter3D::ParticleEmitter3D", + "type": "object" + } + ], + "objectGroups": [] + }, + { + "description": "the min start speed of the object.", + "fullName": "Min start speed", + "functionType": "ExpressionAndCondition", + "group": "3D particle emitter speed configuration", + "name": "StartSpeedMin", + "sentence": "the min start speed", + "events": [ + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [], + "actions": [ + { + "type": { + "value": "SetReturnNumber" + }, + "parameters": [ + "StartSpeedMin" + ] + } + ] + } + ], + "expressionType": { + "type": "expression" + }, + "parameters": [ + { + "description": "Object", + "name": "Object", + "supplementaryInformation": "ParticleEmitter3D::ParticleEmitter3D", + "type": "object" + } + ], + "objectGroups": [] + }, + { + "fullName": "", + "functionType": "ActionWithOperator", + "getterName": "StartSpeedMin", + "group": "3D particle emitter speed configuration", + "name": "SetStartSpeedMin", + "sentence": "", + "events": [ + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [], + "actions": [ + { + "type": { + "value": "SetNumberVariable" + }, + "parameters": [ + "StartSpeedMin", + "=", + "Value" + ] + } + ] + }, + { + "type": "BuiltinCommonInstructions::JsCode", + "inlineCode": [ + "const object = objects[0];\r", + "const startSpeedMin = eventsFunctionContext.getArgument(\"Value\");\r", + "\r", + "object.__particleEmitterAdapter.setStartSpeedMin(startSpeedMin);" + ], + "parameterObjects": "Object", + "useStrict": true, + "eventsSheetExpanded": false + } + ], + "parameters": [ + { + "description": "Object", + "name": "Object", + "supplementaryInformation": "ParticleEmitter3D::ParticleEmitter3D", + "type": "object" + } + ], + "objectGroups": [] + }, + { + "description": "the max start speed of the object.", + "fullName": "Max start speed", + "functionType": "ExpressionAndCondition", + "group": "3D particle emitter speed configuration", + "name": "StartSpeedMax", + "sentence": "the max start speed", + "events": [ + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [], + "actions": [ + { + "type": { + "value": "SetReturnNumber" + }, + "parameters": [ + "StartSpeedMax" + ] + } + ] + } + ], + "expressionType": { + "type": "expression" + }, + "parameters": [ + { + "description": "Object", + "name": "Object", + "supplementaryInformation": "ParticleEmitter3D::ParticleEmitter3D", + "type": "object" + } + ], + "objectGroups": [] + }, + { + "fullName": "", + "functionType": "ActionWithOperator", + "getterName": "StartSpeedMax", + "group": "3D particle emitter speed configuration", + "name": "SetStartSpeedMax", + "sentence": "", + "events": [ + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [], + "actions": [ + { + "type": { + "value": "SetNumberVariable" + }, + "parameters": [ + "StartSpeedMax", + "=", + "Value" + ] + } + ] + }, + { + "type": "BuiltinCommonInstructions::JsCode", + "inlineCode": [ + "const object = objects[0];\r", + "const startSpeedMax = eventsFunctionContext.getArgument(\"Value\");\r", + "\r", + "object.__particleEmitterAdapter.setStartSpeedMax(startSpeedMax);" + ], + "parameterObjects": "Object", + "useStrict": true, + "eventsSheetExpanded": false + } + ], + "parameters": [ + { + "description": "Object", + "name": "Object", + "supplementaryInformation": "ParticleEmitter3D::ParticleEmitter3D", + "type": "object" + } + ], + "objectGroups": [] + }, + { + "description": "the min lifespan of the object.", + "fullName": "Min lifespan", + "functionType": "ExpressionAndCondition", + "group": "3D particle emitter configuration", + "name": "LifespanMin", + "sentence": "the min lifespan", + "events": [ + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [], + "actions": [ + { + "type": { + "value": "SetReturnNumber" + }, + "parameters": [ + "LifespanMin" + ] + } + ] + } + ], + "expressionType": { + "type": "expression" + }, + "parameters": [ + { + "description": "Object", + "name": "Object", + "supplementaryInformation": "ParticleEmitter3D::ParticleEmitter3D", + "type": "object" + } + ], + "objectGroups": [] + }, + { + "fullName": "", + "functionType": "ActionWithOperator", + "getterName": "LifespanMin", + "group": "3D particle emitter configuration", + "name": "SetLifespanMin", + "sentence": "", + "events": [ + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [], + "actions": [ + { + "type": { + "value": "SetNumberVariable" + }, + "parameters": [ + "LifespanMin", + "=", + "Value" + ] + } + ] + }, + { + "type": "BuiltinCommonInstructions::JsCode", + "inlineCode": [ + "const object = objects[0];\r", + "const lifespanMin = eventsFunctionContext.getArgument(\"Value\");\r", + "\r", + "object.__particleEmitterAdapter.setLifespanMin(lifespanMin);\r", + "" + ], + "parameterObjects": "Object", + "useStrict": true, + "eventsSheetExpanded": false + } + ], + "parameters": [ + { + "description": "Object", + "name": "Object", + "supplementaryInformation": "ParticleEmitter3D::ParticleEmitter3D", + "type": "object" + } + ], + "objectGroups": [] + }, + { + "description": "the max lifespan of the object.", + "fullName": "Max lifespan", + "functionType": "ExpressionAndCondition", + "group": "3D particle emitter configuration", + "name": "LifespanMax", + "sentence": "the max lifespan", + "events": [ + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [], + "actions": [ + { + "type": { + "value": "SetReturnNumber" + }, + "parameters": [ + "LifespanMax" + ] + } + ] + } + ], + "expressionType": { + "type": "expression" + }, + "parameters": [ + { + "description": "Object", + "name": "Object", + "supplementaryInformation": "ParticleEmitter3D::ParticleEmitter3D", + "type": "object" + } + ], + "objectGroups": [] + }, + { + "fullName": "", + "functionType": "ActionWithOperator", + "getterName": "LifespanMax", + "group": "3D particle emitter configuration", + "name": "SetLifespanMax", + "sentence": "", + "events": [ + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [], + "actions": [ + { + "type": { + "value": "SetNumberVariable" + }, + "parameters": [ + "LifespanMax", + "=", + "Value" + ] + } + ] + }, + { + "type": "BuiltinCommonInstructions::JsCode", + "inlineCode": [ + "const object = objects[0];\r", + "const lifespanMax = eventsFunctionContext.getArgument(\"Value\");\r", + "\r", + "object.__particleEmitterAdapter.setLifespanMax(lifespanMax);\r", + "" + ], + "parameterObjects": "Object", + "useStrict": true, + "eventsSheetExpanded": false + } + ], + "parameters": [ + { + "description": "Object", + "name": "Object", + "supplementaryInformation": "ParticleEmitter3D::ParticleEmitter3D", + "type": "object" + } + ], + "objectGroups": [] + }, + { + "description": "the emission duration of the object.", + "fullName": "Emission duration", + "functionType": "ExpressionAndCondition", + "group": "3D particle emitter configuration", + "name": "Duration", + "sentence": "the emission duration", + "events": [ + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [], + "actions": [ + { + "type": { + "value": "SetReturnNumber" + }, + "parameters": [ + "Duration" + ] + } + ] + } + ], + "expressionType": { + "type": "expression" + }, + "parameters": [ + { + "description": "Object", + "name": "Object", + "supplementaryInformation": "ParticleEmitter3D::ParticleEmitter3D", + "type": "object" + } + ], + "objectGroups": [] + }, + { + "fullName": "", + "functionType": "ActionWithOperator", + "getterName": "Duration", + "group": "3D particle emitter configuration", + "name": "SetDuration", + "sentence": "", + "events": [ + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [], + "actions": [ + { + "type": { + "value": "SetNumberVariable" + }, + "parameters": [ + "Duration", + "=", + "Value" + ] + } + ] + }, + { + "type": "BuiltinCommonInstructions::JsCode", + "inlineCode": [ + "const object = objects[0];\r", + "const duration = eventsFunctionContext.getArgument(\"Value\");\r", + "\r", + "object.__particleEmitterAdapter.setDuration(duration);\r", + "" + ], + "parameterObjects": "Object", + "useStrict": true, + "eventsSheetExpanded": false + } + ], + "parameters": [ + { + "description": "Object", + "name": "Object", + "supplementaryInformation": "ParticleEmitter3D::ParticleEmitter3D", + "type": "object" + } + ], + "objectGroups": [] + }, + { + "description": "Check if particles move with the emitter.", + "fullName": "Particles move with the emitter", + "functionType": "Condition", + "group": "3D particle emitter speed configuration", + "name": "AreParticlesRelative", + "sentence": "_PARAM0_ particles move with the emitter", + "events": [ + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [ + { + "type": { + "value": "BooleanVariable" + }, + "parameters": [ + "AreParticlesRelative", + "True", + "" + ] + } + ], + "actions": [ + { + "type": { + "value": "SetReturnBoolean" + }, + "parameters": [ + "True" + ] + } + ] + } + ], + "parameters": [ + { + "description": "Object", + "name": "Object", + "supplementaryInformation": "ParticleEmitter3D::ParticleEmitter3D", + "type": "object" + } + ], + "objectGroups": [] + }, + { + "description": "Change if particles move with the emitter.", + "fullName": "Particles move with the emitter", + "functionType": "Action", + "group": "3D particle emitter speed configuration", + "name": "SetAreParticlesRelative", + "sentence": "_PARAM0_ particles move with the emitter: _PARAM1_", + "events": [ + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [ + { + "type": { + "inverted": true, + "value": "BooleanVariable" + }, + "parameters": [ + "Value", + "True", + "" + ] + } + ], + "actions": [ + { + "type": { + "value": "SetBooleanVariable" + }, + "parameters": [ + "AreParticlesRelative", + "False", + "" + ] + } + ] + }, + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [ + { + "type": { + "value": "BooleanVariable" + }, + "parameters": [ + "Value", + "True", + "" + ] + } + ], + "actions": [ + { + "type": { + "value": "SetBooleanVariable" + }, + "parameters": [ + "AreParticlesRelative", + "True", + "" + ] + } + ] + }, + { + "type": "BuiltinCommonInstructions::JsCode", + "inlineCode": [ + "const object = objects[0];\r", + "const areParticlesRelative = eventsFunctionContext.getArgument(\"Value\");\r", + "\r", + "object.__particleEmitterAdapter.setParticlesRelative(areParticlesRelative);\r", + "" + ], + "parameterObjects": "Object", + "useStrict": true, + "eventsSheetExpanded": false + } + ], + "parameters": [ + { + "description": "Object", + "name": "Object", + "supplementaryInformation": "ParticleEmitter3D::ParticleEmitter3D", + "type": "object" + }, + { + "defaultValue": "yes", + "description": "AreParticlesRelative", + "name": "Value", + "optional": true, + "type": "yesorno" + } + ], + "objectGroups": [] + }, + { + "description": "the spay cone angle of the object.", + "fullName": "Spay cone angle", + "functionType": "ExpressionAndCondition", + "group": "3D particle emitter configuration", + "name": "SpayConeAngle", + "sentence": "the spay cone angle", + "events": [ + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [], + "actions": [ + { + "type": { + "value": "SetReturnNumber" + }, + "parameters": [ + "SpayConeAngle" + ] + } + ] + } + ], + "expressionType": { + "type": "expression" + }, + "parameters": [ + { + "description": "Object", + "name": "Object", + "supplementaryInformation": "ParticleEmitter3D::ParticleEmitter3D", + "type": "object" + } + ], + "objectGroups": [] + }, + { + "fullName": "", + "functionType": "ActionWithOperator", + "getterName": "SpayConeAngle", + "group": "3D particle emitter configuration", + "name": "SetSpayConeAngle", + "sentence": "", + "events": [ + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [], + "actions": [ + { + "type": { + "value": "SetNumberVariable" + }, + "parameters": [ + "SpayConeAngle", + "=", + "Value" + ] + } + ] + }, + { + "type": "BuiltinCommonInstructions::JsCode", + "inlineCode": [ + "const object = objects[0];\r", + "const sprayConeAngle = eventsFunctionContext.getArgument(\"Value\");\r", + "\r", + "object.__particleEmitterAdapter.setSprayConeAngle(sprayConeAngle);\r", + "" + ], + "parameterObjects": "Object", + "useStrict": true, + "eventsSheetExpanded": true + } + ], + "parameters": [ + { + "description": "Object", + "name": "Object", + "supplementaryInformation": "ParticleEmitter3D::ParticleEmitter3D", + "type": "object" + } + ], + "objectGroups": [] + }, + { + "description": "the blending of the object.", + "fullName": "Blending", + "functionType": "ExpressionAndCondition", + "group": "3D particle emitter color configuration", + "name": "Blending", + "sentence": "the blending", + "events": [ + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [], + "actions": [ + { + "type": { + "value": "SetReturnString" + }, + "parameters": [ + "Blending" + ] + } + ] + } + ], + "expressionType": { + "supplementaryInformation": "[\"Normal\",\"Additive\",\"Substractive\",\"Multiply\",\"None\"]", + "type": "stringWithSelector" + }, + "parameters": [ + { + "description": "Object", + "name": "Object", + "supplementaryInformation": "ParticleEmitter3D::ParticleEmitter3D", + "type": "object" + } + ], + "objectGroups": [] + }, + { + "fullName": "", + "functionType": "ActionWithOperator", + "getterName": "Blending", + "group": "3D particle emitter color configuration", + "name": "SetBlending", + "private": true, + "sentence": "", + "events": [ + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [], + "actions": [ + { + "type": { + "value": "SetStringVariable" + }, + "parameters": [ + "Blending", + "=", + "Value" + ] + } + ] + }, + { + "type": "BuiltinCommonInstructions::JsCode", + "inlineCode": [ + "const object = objects[0];\r", + "const blending = eventsFunctionContext.getArgument(\"Value\");\r", + "\r", + "object.__particleEmitterAdapter.setBlending(blending);\r", + "" + ], + "parameterObjects": "Object", + "useStrict": true, + "eventsSheetExpanded": true + } + ], + "parameters": [ + { + "description": "Object", + "name": "Object", + "supplementaryInformation": "ParticleEmitter3D::ParticleEmitter3D", + "type": "object" + } + ], + "objectGroups": [] + }, + { + "description": "the gravity top of the object.", + "fullName": "Gravity top", + "functionType": "ExpressionAndCondition", + "group": "3D particle emitter speed configuration", + "name": "GravityTop", + "sentence": "the gravity top", + "events": [ + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [], + "actions": [ + { + "type": { + "value": "SetReturnString" + }, + "parameters": [ + "GravityTop" + ] + } + ] + } + ], + "expressionType": { + "supplementaryInformation": "[\"Y-\",\"Z+\"]", + "type": "stringWithSelector" + }, + "parameters": [ + { + "description": "Object", + "name": "Object", + "supplementaryInformation": "ParticleEmitter3D::ParticleEmitter3D", + "type": "object" + } + ], + "objectGroups": [] + }, + { + "fullName": "", + "functionType": "ActionWithOperator", + "getterName": "GravityTop", + "group": "3D particle emitter speed configuration", + "name": "SetGravityTop", + "sentence": "", + "events": [ + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [], + "actions": [ + { + "type": { + "value": "SetStringVariable" + }, + "parameters": [ + "GravityTop", + "=", + "Value" + ] + } + ] + }, + { + "type": "BuiltinCommonInstructions::JsCode", + "inlineCode": [ + "const object = objects[0];\r", + "const gravityTop = eventsFunctionContext.getArgument(\"Value\");\r", + "\r", + "object.__particleEmitterAdapter.setGravityTop(gravityTop);\r", + "" + ], + "parameterObjects": "Object", + "useStrict": true, + "eventsSheetExpanded": true + } + ], + "parameters": [ + { + "description": "Object", + "name": "Object", + "supplementaryInformation": "ParticleEmitter3D::ParticleEmitter3D", + "type": "object" + } + ], + "objectGroups": [] + }, + { + "description": "the gravity of the object.", + "fullName": "Gravity", + "functionType": "ExpressionAndCondition", + "group": "3D particle emitter speed configuration", + "name": "Gravity", + "sentence": "the gravity", + "events": [ + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [], + "actions": [ + { + "type": { + "value": "SetReturnNumber" + }, + "parameters": [ + "Gravity" + ] + } + ] + } + ], + "expressionType": { + "type": "expression" + }, + "parameters": [ + { + "description": "Object", + "name": "Object", + "supplementaryInformation": "ParticleEmitter3D::ParticleEmitter3D", + "type": "object" + } + ], + "objectGroups": [] + }, + { + "fullName": "", + "functionType": "ActionWithOperator", + "getterName": "Gravity", + "group": "3D particle emitter speed configuration", + "name": "SetGravity", + "sentence": "", + "events": [ + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [], + "actions": [ + { + "type": { + "value": "SetNumberVariable" + }, + "parameters": [ + "Gravity", + "=", + "Value" + ] + } + ] + }, + { + "type": "BuiltinCommonInstructions::JsCode", + "inlineCode": [ + "const object = objects[0];\r", + "const gravity = eventsFunctionContext.getArgument(\"Value\");\r", + "\r", + "object.__particleEmitterAdapter.setGravity(gravity);\r", + "" + ], + "parameterObjects": "Object", + "useStrict": true, + "eventsSheetExpanded": true + } + ], + "parameters": [ + { + "description": "Object", + "name": "Object", + "supplementaryInformation": "ParticleEmitter3D::ParticleEmitter3D", + "type": "object" + } + ], + "objectGroups": [] + }, + { + "description": "Check if delete when emission ends.", + "fullName": "Delete when emission ends", + "functionType": "Condition", + "group": "3D particle emitter configuration", + "name": "ShouldAutodestruct", + "sentence": "_PARAM0_ delete when emission ends", + "events": [ + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [ + { + "type": { + "value": "BooleanVariable" + }, + "parameters": [ + "ShouldAutodestruct", + "True", + "" + ] + } + ], + "actions": [ + { + "type": { + "value": "SetReturnBoolean" + }, + "parameters": [ + "True" + ] + } + ] + } + ], + "parameters": [ + { + "description": "Object", + "name": "Object", + "supplementaryInformation": "ParticleEmitter3D::ParticleEmitter3D", + "type": "object" + } + ], + "objectGroups": [] + }, + { + "description": "Change if delete when emission ends.", + "fullName": "Delete when emission ends", + "functionType": "Action", + "group": "3D particle emitter configuration", + "name": "SetShouldAutodestruct", + "sentence": "_PARAM0_ delete when emission ends: _PARAM1_", + "events": [ + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [ + { + "type": { + "inverted": true, + "value": "BooleanVariable" + }, + "parameters": [ + "Value", + "True", + "" + ] + } + ], + "actions": [ + { + "type": { + "value": "SetBooleanVariable" + }, + "parameters": [ + "ShouldAutodestruct", + "False", + "" + ] + } + ] + }, + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [ + { + "type": { + "value": "BooleanVariable" + }, + "parameters": [ + "Value", + "True", + "" + ] + } + ], + "actions": [ + { + "type": { + "value": "SetBooleanVariable" + }, + "parameters": [ + "ShouldAutodestruct", + "True", + "" + ] + } + ] + } + ], + "parameters": [ + { + "description": "Object", + "name": "Object", + "supplementaryInformation": "ParticleEmitter3D::ParticleEmitter3D", + "type": "object" + }, + { + "defaultValue": "yes", + "description": "ShouldAutodestruct", + "name": "Value", + "optional": true, + "type": "yesorno" + } + ], + "objectGroups": [] + }, + { + "description": "the start min trail length of the object.", + "fullName": "Start min trail length", + "functionType": "ExpressionAndCondition", + "group": "3D particle emitter trail configuration", + "name": "TrailStartLengthMin", + "sentence": "the start min trail length", + "events": [ + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [], + "actions": [ + { + "type": { + "value": "SetReturnNumber" + }, + "parameters": [ + "TrailStartLengthMin" + ] + } + ] + } + ], + "expressionType": { + "type": "expression" + }, + "parameters": [ + { + "description": "Object", + "name": "Object", + "supplementaryInformation": "ParticleEmitter3D::ParticleEmitter3D", + "type": "object" + } + ], + "objectGroups": [] + }, + { + "fullName": "", + "functionType": "ActionWithOperator", + "getterName": "TrailStartLengthMin", + "group": "3D particle emitter trail configuration", + "name": "SetTrailStartLengthMin", + "sentence": "", + "events": [ + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [], + "actions": [ + { + "type": { + "value": "SetNumberVariable" + }, + "parameters": [ + "TrailStartLengthMin", + "=", + "Value" + ] + } + ] + }, + { + "type": "BuiltinCommonInstructions::JsCode", + "inlineCode": [ + "const object = objects[0];\r", + "const value = eventsFunctionContext.getArgument(\"Value\");\r", + "\r", + "object.__particleEmitterAdapter.setTrailStartLengthMin(value);" + ], + "parameterObjects": "Object", + "useStrict": true, + "eventsSheetExpanded": false + } + ], + "parameters": [ + { + "description": "Object", + "name": "Object", + "supplementaryInformation": "ParticleEmitter3D::ParticleEmitter3D", + "type": "object" + } + ], + "objectGroups": [] + }, + { + "description": "the start max trail length of the object.", + "fullName": "Start max trail length", + "functionType": "ExpressionAndCondition", + "group": "3D particle emitter trail configuration", + "name": "TrailStartLengthMax", + "sentence": "the start max trail length", + "events": [ + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [], + "actions": [ + { + "type": { + "value": "SetReturnNumber" + }, + "parameters": [ + "TrailStartLengthMax" + ] + } + ] + } + ], + "expressionType": { + "type": "expression" + }, + "parameters": [ + { + "description": "Object", + "name": "Object", + "supplementaryInformation": "ParticleEmitter3D::ParticleEmitter3D", + "type": "object" + } + ], + "objectGroups": [] + }, + { + "fullName": "", + "functionType": "ActionWithOperator", + "getterName": "TrailStartLengthMax", + "group": "3D particle emitter trail configuration", + "name": "SetTrailStartLengthMax", + "sentence": "", + "events": [ + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [], + "actions": [ + { + "type": { + "value": "SetNumberVariable" + }, + "parameters": [ + "TrailStartLengthMax", + "=", + "Value" + ] + } + ] + }, + { + "type": "BuiltinCommonInstructions::JsCode", + "inlineCode": [ + "const object = objects[0];\r", + "const value = eventsFunctionContext.getArgument(\"Value\");\r", + "\r", + "object.__particleEmitterAdapter.setTrailStartLengthMax(value);" + ], + "parameterObjects": "Object", + "useStrict": true, + "eventsSheetExpanded": false + } + ], + "parameters": [ + { + "description": "Object", + "name": "Object", + "supplementaryInformation": "ParticleEmitter3D::ParticleEmitter3D", + "type": "object" + } + ], + "objectGroups": [] + }, + { + "description": "Check if the trail should follow the object.", + "fullName": "Trail following the object", + "functionType": "Condition", + "group": "3D particle emitter trail configuration", + "name": "IsTrailFollowingLocalOrigin", + "sentence": "The trail is following _PARAM0_ ", + "events": [ + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [ + { + "type": { + "value": "BooleanVariable" + }, + "parameters": [ + "IsTrailFollowingLocalOrigin", + "True", + "" + ] + } + ], + "actions": [ + { + "type": { + "value": "SetReturnBoolean" + }, + "parameters": [ + "True" + ] + } + ] + } + ], + "parameters": [ + { + "description": "Object", + "name": "Object", + "supplementaryInformation": "ParticleEmitter3D::ParticleEmitter3D", + "type": "object" + } + ], + "objectGroups": [] + }, + { + "description": "Change if the trail should follow the object or not.", + "fullName": "Make the trail follow", + "functionType": "Action", + "group": "3D particle emitter trail configuration", + "name": "SetIsTrailFollowingLocalOrigin", + "sentence": "Make the trail follow _PARAM0_: _PARAM1_", + "events": [ + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [ + { + "type": { + "value": "BooleanVariable" + }, + "parameters": [ + "Value", + "False", + "" + ] + } + ], + "actions": [ + { + "type": { + "value": "SetBooleanVariable" + }, + "parameters": [ + "IsTrailFollowingLocalOrigin", + "False", + "" + ] + } + ] + }, + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [ + { + "type": { + "value": "BooleanVariable" + }, + "parameters": [ + "Value", + "True", + "" + ] + } + ], + "actions": [ + { + "type": { + "value": "SetBooleanVariable" + }, + "parameters": [ + "IsTrailFollowingLocalOrigin", + "True", + "" + ] + } + ] + }, + { + "type": "BuiltinCommonInstructions::JsCode", + "inlineCode": [ + "const object = objects[0];\r", + "const value = eventsFunctionContext.getArgument(\"Value\");\r", + "\r", + "object.__particleEmitterAdapter.setTrailFollowingLocalOrigin(value);" + ], + "parameterObjects": "Object", + "useStrict": true, + "eventsSheetExpanded": false + } + ], + "parameters": [ + { + "description": "Object", + "name": "Object", + "supplementaryInformation": "ParticleEmitter3D::ParticleEmitter3D", + "type": "object" + }, + { + "defaultValue": "yes", + "description": "IsTrailFollowingLocalOrigin", + "name": "Value", + "optional": true, + "type": "yesorno" + } + ], + "objectGroups": [] + }, + { + "description": "the tail end width ratio of the object.", + "fullName": "Tail end width ratio", + "functionType": "ExpressionAndCondition", + "group": "3D particle emitter trail configuration", + "name": "TrailEndWidthRatio", + "sentence": "the tail end width ratio", + "events": [ + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [], + "actions": [ + { + "type": { + "value": "SetReturnNumber" + }, + "parameters": [ + "TrailEndWidthRatio" + ] + } + ] + } + ], + "expressionType": { + "type": "expression" + }, + "parameters": [ + { + "description": "Object", + "name": "Object", + "supplementaryInformation": "ParticleEmitter3D::ParticleEmitter3D", + "type": "object" + } + ], + "objectGroups": [] + }, + { + "fullName": "", + "functionType": "ActionWithOperator", + "getterName": "TrailEndWidthRatio", + "group": "3D particle emitter trail configuration", + "name": "SetTrailEndWidthRatio", + "sentence": "", + "events": [ + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [], + "actions": [ + { + "type": { + "value": "SetNumberVariable" + }, + "parameters": [ + "TrailEndWidthRatio", + "=", + "Value" + ] + } + ] + }, + { + "type": "BuiltinCommonInstructions::JsCode", + "inlineCode": [ + "const object = objects[0];\r", + "const value = eventsFunctionContext.getArgument(\"Value\");\r", + "\r", + "object.__particleEmitterAdapter.setTrailEndWidthRatio(value);" + ], + "parameterObjects": "Object", + "useStrict": true, + "eventsSheetExpanded": false + } + ], + "parameters": [ + { + "description": "Object", + "name": "Object", + "supplementaryInformation": "ParticleEmitter3D::ParticleEmitter3D", + "type": "object" + } + ], + "objectGroups": [] + }, + { + "description": "the render mode of the object.", + "fullName": "Render mode", + "functionType": "ExpressionAndCondition", + "group": "3D particle emitter configuration", + "name": "RenderMode", + "private": true, + "sentence": "the render mode", + "events": [ + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [], + "actions": [ + { + "type": { + "value": "SetReturnString" + }, + "parameters": [ + "RenderMode" + ] + } + ] + } + ], + "expressionType": { + "supplementaryInformation": "[\"Billboard\",\"Trail\"]", + "type": "stringWithSelector" + }, + "parameters": [ + { + "description": "Object", + "name": "Object", + "supplementaryInformation": "ParticleEmitter3D::ParticleEmitter3D", + "type": "object" + } + ], + "objectGroups": [] + }, + { + "fullName": "", + "functionType": "ActionWithOperator", + "getterName": "RenderMode", + "group": "3D particle emitter configuration", + "name": "SetRenderMode", + "private": true, + "sentence": "", + "events": [ + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [], + "actions": [ + { + "type": { + "value": "SetStringVariable" + }, + "parameters": [ + "RenderMode", + "=", + "Value" + ] + } + ] + }, + { + "type": "BuiltinCommonInstructions::JsCode", + "inlineCode": [ + "const object = objects[0];\r", + "const value = eventsFunctionContext.getArgument(\"Value\");\r", + "\r", + "object.__particleEmitterAdapter.setRenderMode(value);" + ], + "parameterObjects": "Object", + "useStrict": true, + "eventsSheetExpanded": false + }, + { + "type": "BuiltinCommonInstructions::Comment", + "color": { + "b": 109, + "g": 230, + "r": 255, + "textB": 0, + "textG": 0, + "textR": 0 + }, + "comment": "Update the trail properties because the RenderMode setter reset them" + }, + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [ + { + "type": { + "value": "StringVariable" + }, + "parameters": [ + "RenderMode", + "=", + "\"Trail\"" + ] + } + ], + "actions": [ + { + "type": { + "value": "ParticleEmitter3D::ParticleEmitter3D::SetTrailStartLengthMin" + }, + "parameters": [ + "Object", + "=", + "TrailStartLengthMin", + "" + ] + }, + { + "type": { + "value": "ParticleEmitter3D::ParticleEmitter3D::SetTrailStartLengthMax" + }, + "parameters": [ + "Object", + "=", + "TrailStartLengthMax", + "" + ] + } + ] + } + ], + "parameters": [ + { + "description": "Object", + "name": "Object", + "supplementaryInformation": "ParticleEmitter3D::ParticleEmitter3D", + "type": "object" + } + ], + "objectGroups": [] + } + ], + "eventsFunctionsFolderStructure": { + "folderName": "__ROOT", + "children": [ + { + "functionName": "onCreated" + }, + { + "functionName": "onDestroy" + }, + { + "functionName": "onHotReloading" + }, + { + "functionName": "UpdateFromProperties" + }, + { + "functionName": "UpdateHelper" + }, + { + "functionName": "UpdateImage" + }, + { + "functionName": "doStepPostEvents" + }, + { + "functionName": "RegisterInLayer" + }, + { + "functionName": "Delete" + }, + { + "functionName": "HasEnded" + }, + { + "functionName": "Restart" + }, + { + "folderName": "Position", + "children": [ + { + "functionName": "Z" + }, + { + "functionName": "SetZ" + } + ] + }, + { + "folderName": "Angle", + "children": [ + { + "functionName": "RotationX" + }, + { + "functionName": "SetRotationX" + }, + { + "functionName": "RotationY" + }, + { + "functionName": "SetRotationY" + } + ] + }, + { + "folderName": "3D particle emitter color configuration", + "children": [ + { + "functionName": "StartColor" + }, + { + "functionName": "SetStartColor" + }, + { + "functionName": "EndColor" + }, + { + "functionName": "SetEndColor" + }, + { + "functionName": "StartOpacity" + }, + { + "functionName": "SetStartOpacity" + }, + { + "functionName": "EndOpacity" + }, + { + "functionName": "SetEndOpacity" + }, + { + "functionName": "Blending" + }, + { + "functionName": "SetBlending" + } + ] + }, + { + "folderName": "3D particle emitter configuration", + "children": [ + { + "functionName": "Flow" + }, + { + "functionName": "SetFlow" + }, + { + "functionName": "StartSizeMin" + }, + { + "functionName": "SetStartSizeMin" + }, + { + "functionName": "StartSizeMax" + }, + { + "functionName": "SetStartSizeMax" + }, + { + "functionName": "EndScale" + }, + { + "functionName": "SetEndScale" + }, + { + "functionName": "LifespanMin" + }, + { + "functionName": "SetLifespanMin" + }, + { + "functionName": "LifespanMax" + }, + { + "functionName": "SetLifespanMax" + }, + { + "functionName": "Duration" + }, + { + "functionName": "SetDuration" + }, + { + "functionName": "SpayConeAngle" + }, + { + "functionName": "SetSpayConeAngle" + }, + { + "functionName": "ShouldAutodestruct" + }, + { + "functionName": "SetShouldAutodestruct" + }, + { + "functionName": "RenderMode" + }, + { + "functionName": "SetRenderMode" + } + ] + }, + { + "folderName": "3D particle emitter speed configuration", + "children": [ + { + "functionName": "StartSpeedMin" + }, + { + "functionName": "SetStartSpeedMin" + }, + { + "functionName": "StartSpeedMax" + }, + { + "functionName": "SetStartSpeedMax" + }, + { + "functionName": "AreParticlesRelative" + }, + { + "functionName": "SetAreParticlesRelative" + }, + { + "functionName": "GravityTop" + }, + { + "functionName": "SetGravityTop" + }, + { + "functionName": "Gravity" + }, + { + "functionName": "SetGravity" + } + ] + }, + { + "folderName": "3D particle emitter trail configuration", + "children": [ + { + "functionName": "TrailStartLengthMin" + }, + { + "functionName": "SetTrailStartLengthMin" + }, + { + "functionName": "TrailStartLengthMax" + }, + { + "functionName": "SetTrailStartLengthMax" + }, + { + "functionName": "IsTrailFollowingLocalOrigin" + }, + { + "functionName": "SetIsTrailFollowingLocalOrigin" + }, + { + "functionName": "TrailEndWidthRatio" + }, + { + "functionName": "SetTrailEndWidthRatio" + } + ] + } + ] + }, + "propertyDescriptors": [ + { + "value": "255;0;0", + "type": "Color", + "label": "Start color", + "group": "Color", + "name": "StartColor" + }, + { + "value": "255;255;0", + "type": "Color", + "label": "End color", + "group": "Color", + "name": "EndColor" + }, + { + "value": "255", + "type": "Number", + "unit": "Dimensionless", + "label": "Start opacity", + "group": "Color", + "name": "StartOpacity" + }, + { + "value": "0", + "type": "Number", + "unit": "Dimensionless", + "label": "End opacity", + "group": "Color", + "name": "EndOpacity" + }, + { + "value": "50", + "type": "Number", + "label": "Flow of particles (particles per second)", + "name": "Flow" + }, + { + "value": "10", + "type": "Number", + "unit": "Pixel", + "label": "Start min size", + "group": "Size", + "name": "StartSizeMin" + }, + { + "value": "20", + "type": "Number", + "unit": "Pixel", + "label": "Start max size", + "group": "Size", + "name": "StartSizeMax" + }, + { + "value": "0", + "type": "Number", + "unit": "Dimensionless", + "label": "End scale", + "group": "Size", + "name": "EndScale" + }, + { + "value": "100", + "type": "Number", + "unit": "PixelSpeed", + "label": "Start min speed", + "group": "Speed", + "name": "StartSpeedMin" + }, + { + "value": "100", + "type": "Number", + "unit": "PixelSpeed", + "label": "Start max speed", + "group": "Speed", + "name": "StartSpeedMax" + }, + { + "value": "1", + "type": "Number", + "unit": "Second", + "label": "Min lifespan", + "name": "LifespanMin" + }, + { + "value": "2", + "type": "Number", + "unit": "Second", + "label": "Max lifespan", + "name": "LifespanMax" + }, + { + "value": "5", + "type": "Number", + "unit": "Second", + "label": "Emission duration", + "name": "Duration" + }, + { + "value": "", + "type": "Boolean", + "label": "Particles move with the emitter", + "group": "Speed", + "name": "AreParticlesRelative" + }, + { + "value": "30", + "type": "Number", + "unit": "DegreeAngle", + "label": "Spay cone angle", + "group": "Position", + "name": "SpayConeAngle" + }, + { + "value": "Additive", + "type": "Choice", + "label": "Blending", + "group": "Color", + "choices": [ + { + "label": "Normal", + "value": "Normal" + }, + { + "label": "Additive", + "value": "Additive" + }, + { + "label": "Subtractive", + "value": "Subtractive" + }, + { + "label": "Multiply", + "value": "Multiply" + }, + { + "label": "None", + "value": "None" + } + ], + "name": "Blending" + }, + { + "value": "Y-", + "type": "Choice", + "label": "Gravity top", + "group": "Speed", + "choices": [ + { + "label": "Y-", + "value": "Y-" + }, + { + "label": "Z+", + "value": "Z+" + } + ], + "name": "GravityTop" + }, + { + "value": "0", + "type": "Number", + "unit": "PixelAcceleration", + "label": "Gravity", + "group": "Speed", + "name": "Gravity" + }, + { + "value": "true", + "type": "Boolean", + "label": "Delete when emission ends", + "name": "ShouldAutodestruct" + }, + { + "value": "Center-center", + "type": "String", + "label": "", + "description": "Only used by the scene editor.", + "hidden": true, + "name": "ParentOrigin" + }, + { + "value": "0", + "type": "Number", + "unit": "Pixel", + "label": "Z (elevation)", + "description": "Deprecated", + "group": "Position", + "hidden": true, + "name": "Z" + }, + { + "value": "0", + "type": "Number", + "unit": "DegreeAngle", + "label": "Rotation on X axis", + "group": "Position", + "hidden": true, + "name": "RotationX" + }, + { + "value": "0", + "type": "Number", + "unit": "DegreeAngle", + "label": "Rotation on Y axis", + "group": "Position", + "hidden": true, + "name": "RotationY" + }, + { + "value": "120", + "type": "Number", + "unit": "Pixel", + "label": "Start min length", + "group": "Trail", + "advanced": true, + "name": "TrailStartLengthMin" + }, + { + "value": "120", + "type": "Number", + "unit": "Pixel", + "label": "Start max length", + "group": "Trail", + "advanced": true, + "name": "TrailStartLengthMax" + }, + { + "value": "Billboard", + "type": "Choice", + "label": "Render mode", + "choices": [ + { + "label": "Image facing the camera", + "value": "Billboard" + }, + { + "label": "Image following a trail", + "value": "Trail" + } + ], + "advanced": true, + "name": "RenderMode" + }, + { + "value": "", + "type": "Boolean", + "label": "Follow the object", + "group": "Trail", + "advanced": true, + "name": "IsTrailFollowingLocalOrigin" + }, + { + "value": "0", + "type": "Number", + "unit": "Dimensionless", + "label": "Tail end width ratio", + "group": "Trail", + "advanced": true, + "name": "TrailEndWidthRatio" + } + ], + "propertiesFolderStructure": { + "folderName": "__ROOT", + "children": [ + { + "propertyName": "Flow" + }, + { + "propertyName": "LifespanMin" + }, + { + "propertyName": "LifespanMax" + }, + { + "propertyName": "Duration" + }, + { + "propertyName": "ShouldAutodestruct" + }, + { + "propertyName": "RenderMode" + }, + { + "folderName": "Color", + "children": [ + { + "propertyName": "StartColor" + }, + { + "propertyName": "EndColor" + }, + { + "propertyName": "StartOpacity" + }, + { + "propertyName": "EndOpacity" + }, + { + "propertyName": "Blending" + } + ] + }, + { + "folderName": "Size", + "children": [ + { + "propertyName": "StartSizeMin" + }, + { + "propertyName": "StartSizeMax" + }, + { + "propertyName": "EndScale" + } + ] + }, + { + "folderName": "Trail", + "children": [ + { + "propertyName": "TrailStartLengthMin" + }, + { + "propertyName": "TrailStartLengthMax" + }, + { + "propertyName": "TrailEndWidthRatio" + }, + { + "propertyName": "IsTrailFollowingLocalOrigin" + } + ] + }, + { + "folderName": "Speed", + "children": [ + { + "propertyName": "StartSpeedMin" + }, + { + "propertyName": "StartSpeedMax" + }, + { + "propertyName": "AreParticlesRelative" + }, + { + "propertyName": "GravityTop" + }, + { + "propertyName": "Gravity" + } + ] + }, + { + "folderName": "Position", + "children": [ + { + "propertyName": "SpayConeAngle" + }, + { + "propertyName": "Z" + }, + { + "propertyName": "RotationX" + }, + { + "propertyName": "RotationY" + } + ] + }, + { + "propertyName": "ParentOrigin" + } + ] + }, + "variants": [] + } + ] + }, + { + "author": "", + "category": "Visual effect", + "dimension": "3D", + "extensionNamespace": "", + "fullName": "3D object shake", + "gdevelopVersion": ">=5.5.222", + "helpPath": "", + "iconUrl": "data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iVVRGLTgiPz48IURPQ1RZUEUgc3ZnIFBVQkxJQyAiLS8vVzNDLy9EVEQgU1ZHIDEuMS8vRU4iICJodHRwOi8vd3d3LnczLm9yZy9HcmFwaGljcy9TVkcvMS4xL0RURC9zdmcxMS5kdGQiPjxzdmcgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIiB4bWxuczp4bGluaz0iaHR0cDovL3d3dy53My5vcmcvMTk5OS94bGluayIgdmVyc2lvbj0iMS4xIiBpZD0ibWRpLXZlY3Rvci1kaWZmZXJlbmNlLWFiIiB3aWR0aD0iMjQiIGhlaWdodD0iMjQiIHZpZXdCb3g9IjAgMCAyNCAyNCI+PHBhdGggZD0iTTMsMUMxLjg5LDEgMSwxLjg5IDEsM1Y1SDNWM0g1VjFIM003LDFWM0gxMFYxSDdNMTIsMVYzSDE0VjVIMTZWM0MxNiwxLjg5IDE1LjExLDEgMTQsMUgxMk0xLDdWMTBIM1Y3SDFNMTQsN0MxNCw3IDE0LDExLjY3IDE0LDE0QzExLjY3LDE0IDcsMTQgNywxNEM3LDE0IDcsMTggNywyMEM3LDIxLjExIDcuODksMjIgOSwyMkgyMEMyMS4xMSwyMiAyMiwyMS4xMSAyMiwyMFY5QzIyLDcuODkgMjEuMTEsNyAyMCw3QzE4LDcgMTQsNyAxNCw3TTE2LDlIMjBWMjBIOVYxNkgxNEMxNS4xMSwxNiAxNiwxNS4xMSAxNiwxNFY5TTEsMTJWMTRDMSwxNS4xMSAxLjg5LDE2IDMsMTZINVYxNEgzVjEySDFaIiAvPjwvc3ZnPg==", + "name": "ShakeObject3D", + "previewIconUrl": "https://resources.gdevelop-app.com/assets/Icons/vector-difference-ab.svg", + "shortDescription": "Shake 3D objects.", + "version": "2.1.1", + "description": [ + "Shake 3D objects with translation and rotation.", + "", + "The 3D racing game example uses this extension ([open the project online](https://editor.gdevelop.io/?project=example://3d-racing-game))." + ], + "origin": { + "identifier": "ShakeObject3D", + "name": "gdevelop-extension-store" + }, + "tags": [ + "3d", + "shaking", + "effect", + "shake", + "translate", + "rotate" + ], + "authorIds": [ + "IWykYNRvhCZBN3vEgKEbBPOR3Oc2" + ], + "changelog": [ + { + "version": "2.0.0", + "breaking": "- The behavior for 3D box has been removed. The other behavior can be used for both models and boxes." + } + ], + "dependencies": [], + "globalVariables": [], + "sceneVariables": [], + "eventsFunctions": [ + { + "fullName": "", + "functionType": "Action", + "name": "onFirstSceneLoaded", + "sentence": "", + "events": [ + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [], + "actions": [ + { + "type": { + "value": "ShakeObject3D::DefineHelperClasses" + }, + "parameters": [ + "", + "" + ] + } + ] + } + ], + "parameters": [], + "objectGroups": [] + }, + { + "description": "Define helper classes JavaScript code.", + "fullName": "Define helper classes", + "functionType": "Action", + "name": "DefineHelperClasses", + "private": true, + "sentence": "Define helper classes JavaScript code", + "events": [ + { + "type": "BuiltinCommonInstructions::JsCode", + "inlineCode": [ + "if (gdjs._shakeObjectExtension) {", + " return;", + "}", + "", + "/** Noise generator manager. */", + "class NoiseManager {", + " /**", + " * Create the manager of noise generators.", + " */", + " constructor() {", + " this.seed = gdjs.randomInRange(1, Number.MAX_SAFE_INTEGER);", + " /** @type {Map} */", + " this.generators = new Map();", + " }", + "", + " /**", + " * @param name {string}", + " * @return {NoiseGenerator}", + " */", + " getGenerator(name) {", + " let generator = this.generators.get(name);", + " if (!generator) {", + " generator = new NoiseGenerator(name + this.seed);", + " this.generators.set(name, generator);", + " }", + " return generator;", + " }", + "", + " /**", + " * @param seed {number}", + " */", + " setSeed(seed) {", + " this.seed = seed;", + " this.generators.forEach(generator => generator.setSeed(name + this.seed));", + " }", + "", + " /**", + " * @param name {string}", + " */", + " deleteGenerator(name) {", + " this.generators.delete(name);", + " }", + "", + " /**", + " */", + " deleteAllGenerators() {", + " this.generators.clear();", + " }", + "}", + "", + "/** Noise generator with octaves. */", + "class NoiseGenerator {", + " /**", + " * Create a noise generator with a seed.", + " * @param seed {string}", + " */", + " constructor(seed) {", + " this.simplexNoise = new SimplexNoise(seed);", + " this.frequency = 1;", + " this.octaves = 1;", + " this.persistence = 0.5;", + " this.lacunarity = 2;", + " this.xLoopPeriod = 0;", + " this.yLoopPeriod = 0;", + " }", + "", + " /**", + " * @param seed {string}", + " */", + " setSeed(seed) {", + " this.simplexNoise = new SimplexNoise(seed);", + " }", + "", + " /**", + " * @param x {float}", + " * @param y {float}", + " * @param z {float} optionnal", + " * @param w {float} optionnal", + " * @return {float}", + " */", + " noise(x, y, z, w) {", + " if (this.xLoopPeriod && this.yLoopPeriod) {", + " const circleRatioX = 2 * Math.PI / this.xLoopPeriod;", + " const circleRatioY = 2 * Math.PI / this.yLoopPeriod;", + " const angleX = circleRatioX * x;", + " const angleY = circleRatioY * y;", + " x = Math.cos(angleX) / circleRatioX;", + " y = Math.sin(angleX) / circleRatioX;", + " z = Math.cos(angleY) / circleRatioY;", + " w = Math.sin(angleY) / circleRatioY;", + " }", + " else if (this.xLoopPeriod) {", + " const circleRatio = 2 * Math.PI / this.xLoopPeriod;", + " const angleX = circleRatio * x;", + " w = z;", + " z = y;", + " x = Math.cos(angleX) / circleRatio;", + " y = Math.sin(angleX) / circleRatio;", + " }", + " else if (this.yLoopPeriod) {", + " const circleRatio = 2 * Math.PI / this.xLoopPeriod;", + " const angleX = circleRatio * x;", + " w = z;", + " // Make the circle perimeter equals to the looping period", + " // to keep the same perceived frequency with or without looping.", + " y = Math.cos(angleX) / circleRatio;", + " z = Math.sin(angleX) / circleRatio;", + " }", + " let noiseFunction = this.simplexNoise.noise4D.bind(this.simplexNoise);", + " if (z === undefined) {", + " noiseFunction = this.simplexNoise.noise2D.bind(this.simplexNoise);", + " }", + " else if (w === undefined) {", + " noiseFunction = this.simplexNoise.noise3D.bind(this.simplexNoise);", + " }", + " let frequency = this.frequency;", + " let noiseSum = 0;", + " let amplitudeSum = 0;", + " let amplitude = 1;", + " for (let i = 0; i < this.octaves; i++) {", + " noiseSum += noiseFunction(x * frequency, y * frequency, z * frequency, w * frequency) * amplitude;", + " amplitudeSum += Math.abs(amplitude);", + " amplitude *= this.persistence;", + " frequency *= this.lacunarity;", + " }", + " return noiseSum / amplitudeSum;", + " }", + "}", + "", + "/*", + "A fast javascript implementation of simplex noise by Jonas Wagner", + "https://github.com/jwagner/simplex-noise.js", + "", + "Based on a speed-improved simplex noise algorithm for 2D, 3D and 4D in Java.", + "Which is based on example code by Stefan Gustavson (stegu@itn.liu.se).", + "With Optimisations by Peter Eastman (peastman@drizzle.stanford.edu).", + "Better rank ordering method by Stefan Gustavson in 2012.", + "", + " Copyright (c) 2021 Jonas Wagner", + "", + " Permission is hereby granted, free of charge, to any person obtaining a copy", + " of this software and associated documentation files (the \"Software\"), to deal", + " in the Software without restriction, including without limitation the rights", + " to use, copy, modify, merge, publish, distribute, sublicense, and/or sell", + " copies of the Software, and to permit persons to whom the Software is", + " furnished to do so, subject to the following conditions:", + "", + " The above copyright notice and this permission notice shall be included in all", + " copies or substantial portions of the Software.", + "", + " THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR", + " IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,", + " FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE", + " AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER", + " LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,", + " OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE", + " SOFTWARE.", + " */", + "", + "const F2 = 0.5 * (Math.sqrt(3.0) - 1.0);", + "const G2 = (3.0 - Math.sqrt(3.0)) / 6.0;", + "const F3 = 1.0 / 3.0;", + "const G3 = 1.0 / 6.0;", + "const F4 = (Math.sqrt(5.0) - 1.0) / 4.0;", + "const G4 = (5.0 - Math.sqrt(5.0)) / 20.0;", + "const grad3 = new Float32Array([1, 1, 0,", + " -1, 1, 0,", + " 1, -1, 0,", + " -1, -1, 0,", + " 1, 0, 1,", + " -1, 0, 1,", + " 1, 0, -1,", + " -1, 0, -1,", + " 0, 1, 1,", + " 0, -1, 1,", + " 0, 1, -1,", + " 0, -1, -1]);", + "const grad4 = new Float32Array([0, 1, 1, 1, 0, 1, 1, -1, 0, 1, -1, 1, 0, 1, -1, -1,", + " 0, -1, 1, 1, 0, -1, 1, -1, 0, -1, -1, 1, 0, -1, -1, -1,", + " 1, 0, 1, 1, 1, 0, 1, -1, 1, 0, -1, 1, 1, 0, -1, -1,", + " -1, 0, 1, 1, -1, 0, 1, -1, -1, 0, -1, 1, -1, 0, -1, -1,", + " 1, 1, 0, 1, 1, 1, 0, -1, 1, -1, 0, 1, 1, -1, 0, -1,", + " -1, 1, 0, 1, -1, 1, 0, -1, -1, -1, 0, 1, -1, -1, 0, -1,", + " 1, 1, 1, 0, 1, 1, -1, 0, 1, -1, 1, 0, 1, -1, -1, 0,", + " -1, 1, 1, 0, -1, 1, -1, 0, -1, -1, 1, 0, -1, -1, -1, 0]);", + "", + "", + "/**", + " * Builds a random permutation table.", + " * This is exported only for (internal) testing purposes.", + " * Do not rely on this export.", + " * @param {() => number} random", + " * @private", + " */", + "function buildPermutationTable(random) {", + " const p = new Uint8Array(256);", + " for (let i = 0; i < 256; i++) {", + " p[i] = i;", + " }", + " for (let i = 0; i < 255; i++) {", + " const r = i + ~~(random() * (256 - i));", + " const aux = p[i];", + " p[i] = p[r];", + " p[r] = aux;", + " }", + " return p;", + "}", + "", + "/*", + "The ALEA PRNG and masher code used by simplex-noise.js", + "is based on code by Johannes Baagøe, modified by Jonas Wagner.", + "See alea.md for the full license.", + "@param {string|number} seed", + "*/", + "function alea(seed) {", + " let s0 = 0;", + " let s1 = 0;", + " let s2 = 0;", + " let c = 1;", + " const mash = masher();", + " s0 = mash(' ');", + " s1 = mash(' ');", + " s2 = mash(' ');", + " s0 -= mash(seed);", + " if (s0 < 0) {", + " s0 += 1;", + " }", + " s1 -= mash(seed);", + " if (s1 < 0) {", + " s1 += 1;", + " }", + " s2 -= mash(seed);", + " if (s2 < 0) {", + " s2 += 1;", + " }", + " return function () {", + " const t = 2091639 * s0 + c * 2.3283064365386963e-10; // 2^-32", + " s0 = s1;", + " s1 = s2;", + " return s2 = t - (c = t | 0);", + " };", + "}", + "", + "function masher() {", + " let n = 0xefc8249d;", + " return function (data) {", + " data = data.toString();", + " for (let i = 0; i < data.length; i++) {", + " n += data.charCodeAt(i);", + " let h = 0.02519603282416938 * n;", + " n = h >>> 0;", + " h -= n;", + " h *= n;", + " n = h >>> 0;", + " h -= n;", + " n += h * 0x100000000; // 2^32", + " }", + " return (n >>> 0) * 2.3283064365386963e-10; // 2^-32", + " };", + "}", + "", + "/** Deterministic simplex noise generator suitable for 2D, 3D and 4D spaces. */", + "class SimplexNoise {", + " /**", + " * Creates a new `SimplexNoise` instance.", + " * This involves some setup. You can save a few cpu cycles by reusing the same instance.", + " * @param {(() => number)|string|number} randomOrSeed A random number generator or a seed (string|number).", + " * Defaults to Math.random (random irreproducible initialization).", + " */", + " constructor(randomOrSeed) {", + " if (randomOrSeed === void 0) { randomOrSeed = Math.random; }", + " const random = typeof randomOrSeed == 'function' ? randomOrSeed : alea(randomOrSeed);", + " this.p = buildPermutationTable(random);", + " this.perm = new Uint8Array(512);", + " this.permMod12 = new Uint8Array(512);", + " for (let i = 0; i < 512; i++) {", + " this.perm[i] = this.p[i & 255];", + " this.permMod12[i] = this.perm[i] % 12;", + " }", + " }", + "", + " /**", + " * Samples the noise field in 2 dimensions", + " * @param {number} x", + " * @param {number} y", + " * @returns a number in the interval [-1, 1]", + " */", + " noise2D(x, y) {", + " const permMod12 = this.permMod12;", + " const perm = this.perm;", + " let n0 = 0; // Noise contributions from the three corners", + " let n1 = 0;", + " let n2 = 0;", + " // Skew the input space to determine which simplex cell we're in", + " const s = (x + y) * F2; // Hairy factor for 2D", + " const i = Math.floor(x + s);", + " const j = Math.floor(y + s);", + " const t = (i + j) * G2;", + " const X0 = i - t; // Unskew the cell origin back to (x,y) space", + " const Y0 = j - t;", + " const x0 = x - X0; // The x,y distances from the cell origin", + " const y0 = y - Y0;", + " // For the 2D case, the simplex shape is an equilateral triangle.", + " // Determine which simplex we are in.", + " let i1, j1; // Offsets for second (middle) corner of simplex in (i,j) coords", + " if (x0 > y0) {", + " i1 = 1;", + " j1 = 0;", + " } // lower triangle, XY order: (0,0)->(1,0)->(1,1)", + " else {", + " i1 = 0;", + " j1 = 1;", + " } // upper triangle, YX order: (0,0)->(0,1)->(1,1)", + " // A step of (1,0) in (i,j) means a step of (1-c,-c) in (x,y), and", + " // a step of (0,1) in (i,j) means a step of (-c,1-c) in (x,y), where", + " // c = (3-sqrt(3))/6", + " const x1 = x0 - i1 + G2; // Offsets for middle corner in (x,y) unskewed coords", + " const y1 = y0 - j1 + G2;", + " const x2 = x0 - 1.0 + 2.0 * G2; // Offsets for last corner in (x,y) unskewed coords", + " const y2 = y0 - 1.0 + 2.0 * G2;", + " // Work out the hashed gradient indices of the three simplex corners", + " const ii = i & 255;", + " const jj = j & 255;", + " // Calculate the contribution from the three corners", + " let t0 = 0.5 - x0 * x0 - y0 * y0;", + " if (t0 >= 0) {", + " const gi0 = permMod12[ii + perm[jj]] * 3;", + " t0 *= t0;", + " n0 = t0 * t0 * (grad3[gi0] * x0 + grad3[gi0 + 1] * y0); // (x,y) of grad3 used for 2D gradient", + " }", + " let t1 = 0.5 - x1 * x1 - y1 * y1;", + " if (t1 >= 0) {", + " const gi1 = permMod12[ii + i1 + perm[jj + j1]] * 3;", + " t1 *= t1;", + " n1 = t1 * t1 * (grad3[gi1] * x1 + grad3[gi1 + 1] * y1);", + " }", + " let t2 = 0.5 - x2 * x2 - y2 * y2;", + " if (t2 >= 0) {", + " const gi2 = permMod12[ii + 1 + perm[jj + 1]] * 3;", + " t2 *= t2;", + " n2 = t2 * t2 * (grad3[gi2] * x2 + grad3[gi2 + 1] * y2);", + " }", + " // Add contributions from each corner to get the final noise value.", + " // The result is scaled to return values in the interval [-1,1].", + " return 70.0 * (n0 + n1 + n2);", + " }", + "", + " /**", + " * Samples the noise field in 3 dimensions", + " * @param {number} x", + " * @param {number} y", + " * @param {number} z", + " * @returns a number in the interval [-1, 1]", + " */", + " noise3D(x, y, z) {", + " const permMod12 = this.permMod12;", + " const perm = this.perm;", + " let n0, n1, n2, n3; // Noise contributions from the four corners", + " // Skew the input space to determine which simplex cell we're in", + " const s = (x + y + z) * F3; // Very nice and simple skew factor for 3D", + " const i = Math.floor(x + s);", + " const j = Math.floor(y + s);", + " const k = Math.floor(z + s);", + " const t = (i + j + k) * G3;", + " const X0 = i - t; // Unskew the cell origin back to (x,y,z) space", + " const Y0 = j - t;", + " const Z0 = k - t;", + " const x0 = x - X0; // The x,y,z distances from the cell origin", + " const y0 = y - Y0;", + " const z0 = z - Z0;", + " // For the 3D case, the simplex shape is a slightly irregular tetrahedron.", + " // Determine which simplex we are in.", + " let i1, j1, k1; // Offsets for second corner of simplex in (i,j,k) coords", + " let i2, j2, k2; // Offsets for third corner of simplex in (i,j,k) coords", + " if (x0 >= y0) {", + " if (y0 >= z0) {", + " i1 = 1;", + " j1 = 0;", + " k1 = 0;", + " i2 = 1;", + " j2 = 1;", + " k2 = 0;", + " } // X Y Z order", + " else if (x0 >= z0) {", + " i1 = 1;", + " j1 = 0;", + " k1 = 0;", + " i2 = 1;", + " j2 = 0;", + " k2 = 1;", + " } // X Z Y order", + " else {", + " i1 = 0;", + " j1 = 0;", + " k1 = 1;", + " i2 = 1;", + " j2 = 0;", + " k2 = 1;", + " } // Z X Y order", + " }", + " else { // x0 y0)", + " rankx++;", + " else", + " ranky++;", + " if (x0 > z0)", + " rankx++;", + " else", + " rankz++;", + " if (x0 > w0)", + " rankx++;", + " else", + " rankw++;", + " if (y0 > z0)", + " ranky++;", + " else", + " rankz++;", + " if (y0 > w0)", + " ranky++;", + " else", + " rankw++;", + " if (z0 > w0)", + " rankz++;", + " else", + " rankw++;", + " // simplex[c] is a 4-vector with the numbers 0, 1, 2 and 3 in some order.", + " // Many values of c will never occur, since e.g. x>y>z>w makes x= 3 ? 1 : 0;", + " const j1 = ranky >= 3 ? 1 : 0;", + " const k1 = rankz >= 3 ? 1 : 0;", + " const l1 = rankw >= 3 ? 1 : 0;", + " // The integer offsets for the third simplex corner", + " const i2 = rankx >= 2 ? 1 : 0;", + " const j2 = ranky >= 2 ? 1 : 0;", + " const k2 = rankz >= 2 ? 1 : 0;", + " const l2 = rankw >= 2 ? 1 : 0;", + " // The integer offsets for the fourth simplex corner", + " const i3 = rankx >= 1 ? 1 : 0;", + " const j3 = ranky >= 1 ? 1 : 0;", + " const k3 = rankz >= 1 ? 1 : 0;", + " const l3 = rankw >= 1 ? 1 : 0;", + " // The fifth corner has all coordinate offsets = 1, so no need to compute that.", + " const x1 = x0 - i1 + G4; // Offsets for second corner in (x,y,z,w) coords", + " const y1 = y0 - j1 + G4;", + " const z1 = z0 - k1 + G4;", + " const w1 = w0 - l1 + G4;", + " const x2 = x0 - i2 + 2.0 * G4; // Offsets for third corner in (x,y,z,w) coords", + " const y2 = y0 - j2 + 2.0 * G4;", + " const z2 = z0 - k2 + 2.0 * G4;", + " const w2 = w0 - l2 + 2.0 * G4;", + " const x3 = x0 - i3 + 3.0 * G4; // Offsets for fourth corner in (x,y,z,w) coords", + " const y3 = y0 - j3 + 3.0 * G4;", + " const z3 = z0 - k3 + 3.0 * G4;", + " const w3 = w0 - l3 + 3.0 * G4;", + " const x4 = x0 - 1.0 + 4.0 * G4; // Offsets for last corner in (x,y,z,w) coords", + " const y4 = y0 - 1.0 + 4.0 * G4;", + " const z4 = z0 - 1.0 + 4.0 * G4;", + " const w4 = w0 - 1.0 + 4.0 * G4;", + " // Work out the hashed gradient indices of the five simplex corners", + " const ii = i & 255;", + " const jj = j & 255;", + " const kk = k & 255;", + " const ll = l & 255;", + " // Calculate the contribution from the five corners", + " let t0 = 0.6 - x0 * x0 - y0 * y0 - z0 * z0 - w0 * w0;", + " if (t0 < 0)", + " n0 = 0.0;", + " else {", + " const gi0 = (perm[ii + perm[jj + perm[kk + perm[ll]]]] % 32) * 4;", + " t0 *= t0;", + " n0 = t0 * t0 * (grad4[gi0] * x0 + grad4[gi0 + 1] * y0 + grad4[gi0 + 2] * z0 + grad4[gi0 + 3] * w0);", + " }", + " let t1 = 0.6 - x1 * x1 - y1 * y1 - z1 * z1 - w1 * w1;", + " if (t1 < 0)", + " n1 = 0.0;", + " else {", + " const gi1 = (perm[ii + i1 + perm[jj + j1 + perm[kk + k1 + perm[ll + l1]]]] % 32) * 4;", + " t1 *= t1;", + " n1 = t1 * t1 * (grad4[gi1] * x1 + grad4[gi1 + 1] * y1 + grad4[gi1 + 2] * z1 + grad4[gi1 + 3] * w1);", + " }", + " let t2 = 0.6 - x2 * x2 - y2 * y2 - z2 * z2 - w2 * w2;", + " if (t2 < 0)", + " n2 = 0.0;", + " else {", + " const gi2 = (perm[ii + i2 + perm[jj + j2 + perm[kk + k2 + perm[ll + l2]]]] % 32) * 4;", + " t2 *= t2;", + " n2 = t2 * t2 * (grad4[gi2] * x2 + grad4[gi2 + 1] * y2 + grad4[gi2 + 2] * z2 + grad4[gi2 + 3] * w2);", + " }", + " let t3 = 0.6 - x3 * x3 - y3 * y3 - z3 * z3 - w3 * w3;", + " if (t3 < 0)", + " n3 = 0.0;", + " else {", + " const gi3 = (perm[ii + i3 + perm[jj + j3 + perm[kk + k3 + perm[ll + l3]]]] % 32) * 4;", + " t3 *= t3;", + " n3 = t3 * t3 * (grad4[gi3] * x3 + grad4[gi3 + 1] * y3 + grad4[gi3 + 2] * z3 + grad4[gi3 + 3] * w3);", + " }", + " let t4 = 0.6 - x4 * x4 - y4 * y4 - z4 * z4 - w4 * w4;", + " if (t4 < 0)", + " n4 = 0.0;", + " else {", + " const gi4 = (perm[ii + 1 + perm[jj + 1 + perm[kk + 1 + perm[ll + 1]]]] % 32) * 4;", + " t4 *= t4;", + " n4 = t4 * t4 * (grad4[gi4] * x4 + grad4[gi4 + 1] * y4 + grad4[gi4 + 2] * z4 + grad4[gi4 + 3] * w4);", + " }", + " // Sum up and scale the result to cover the range [-1,1]", + " return 27.0 * (n0 + n1 + n2 + n3 + n4);", + " };", + "}", + "", + "gdjs._shakeObjectExtension = {", + " noiseManager: new NoiseManager(),", + "};", + "" + ], + "parameterObjects": "", + "useStrict": true, + "eventsSheetExpanded": true + } + ], + "parameters": [], + "objectGroups": [] + }, + { + "description": "Generate a number from 2 dimensional simplex noise.", + "fullName": "2D noise", + "functionType": "Expression", + "name": "Noise2d", + "private": true, + "sentence": "", + "events": [ + { + "type": "BuiltinCommonInstructions::JsCode", + "inlineCode": [ + "const name = eventsFunctionContext.getArgument(\"Name\");\r", + "const x = eventsFunctionContext.getArgument(\"X\");\r", + "const y = eventsFunctionContext.getArgument(\"Y\");\r", + "\r", + "eventsFunctionContext.returnValue = gdjs._shakeObjectExtension.noiseManager.getGenerator(name).noise(x, y);" + ], + "parameterObjects": "", + "useStrict": true, + "eventsSheetExpanded": true + } + ], + "expressionType": { + "type": "expression" + }, + "parameters": [ + { + "description": "Generator name", + "name": "Name", + "type": "string" + }, + { + "description": "X coordinate", + "name": "X", + "type": "expression" + }, + { + "description": "Y coordinate", + "name": "Y", + "type": "expression" + } + ], + "objectGroups": [] + }, + { + "description": "Generate a number from 3 dimensional simplex noise.", + "fullName": "3D noise", + "functionType": "Expression", + "name": "Noise3d", + "private": true, + "sentence": "", + "events": [ + { + "type": "BuiltinCommonInstructions::JsCode", + "inlineCode": [ + "const name = eventsFunctionContext.getArgument(\"Name\");\r", + "const x = eventsFunctionContext.getArgument(\"X\");\r", + "const y = eventsFunctionContext.getArgument(\"Y\");\r", + "const z = eventsFunctionContext.getArgument(\"Z\");\r", + "\r", + "eventsFunctionContext.returnValue = gdjs._shakeObjectExtension.noiseManager.getGenerator(name).noise(x, y, z);" + ], + "parameterObjects": "", + "useStrict": true, + "eventsSheetExpanded": true + } + ], + "expressionType": { + "type": "expression" + }, + "parameters": [ + { + "description": "Generator name", + "name": "Name", + "type": "string" + }, + { + "description": "X coordinate", + "name": "X", + "type": "expression" + }, + { + "description": "Y coordinate", + "name": "Y", + "type": "expression" + }, + { + "description": "Z coordinate", + "name": "Z", + "type": "expression" + } + ], + "objectGroups": [] + }, + { + "description": "Generate a number from 4 dimensional simplex noise.", + "fullName": "4D noise", + "functionType": "Expression", + "name": "Noise4d", + "private": true, + "sentence": "", + "events": [ + { + "type": "BuiltinCommonInstructions::JsCode", + "inlineCode": [ + "const name = eventsFunctionContext.getArgument(\"Name\");\r", + "const x = eventsFunctionContext.getArgument(\"X\");\r", + "const y = eventsFunctionContext.getArgument(\"Y\");\r", + "const z = eventsFunctionContext.getArgument(\"Z\");\r", + "const w = eventsFunctionContext.getArgument(\"W\");\r", + "\r", + "eventsFunctionContext.returnValue = gdjs._shakeObjectExtension.noiseManager.getGenerator(name).noise(x, y, z, w);" + ], + "parameterObjects": "", + "useStrict": true, + "eventsSheetExpanded": true + } + ], + "expressionType": { + "type": "expression" + }, + "parameters": [ + { + "description": "Generator name", + "name": "Name", + "type": "string" + }, + { + "description": "X coordinate", + "name": "X", + "type": "expression" + }, + { + "description": "Y coordinate", + "name": "Y", + "type": "expression" + }, + { + "description": "Z coordinate", + "name": "Z", + "type": "expression" + }, + { + "description": "W coordinate", + "name": "W", + "type": "expression" + } + ], + "objectGroups": [] + }, + { + "description": "Create a noise generator with default settings (frequency = 1, octaves = 1, persistence = 0.5, lacunarity = 2).", + "fullName": "Create a noise generator", + "functionType": "Action", + "name": "Create", + "private": true, + "sentence": "Create a noise generator named _PARAM1_", + "events": [ + { + "type": "BuiltinCommonInstructions::JsCode", + "inlineCode": [ + "const name = eventsFunctionContext.getArgument(\"Name\");\r", + "\r", + "gdjs._shakeObjectExtension.noiseManager.getGenerator(name);" + ], + "parameterObjects": "", + "useStrict": true, + "eventsSheetExpanded": false + } + ], + "parameters": [ + { + "description": "Generator name", + "name": "Name", + "type": "string" + } + ], + "objectGroups": [] + }, + { + "description": "Delete a noise generator and loose its settings.", + "fullName": "Delete a noise generator", + "functionType": "Action", + "name": "Delete", + "private": true, + "sentence": "Delete _PARAM1_ noise generator", + "events": [ + { + "type": "BuiltinCommonInstructions::JsCode", + "inlineCode": [ + "const name = eventsFunctionContext.getArgument(\"Name\");\r", + "\r", + "gdjs._shakeObjectExtension.noiseManager.deleteGenerator(name);" + ], + "parameterObjects": "", + "useStrict": true, + "eventsSheetExpanded": false + } + ], + "parameters": [ + { + "description": "Generator name", + "name": "Name", + "type": "string" + } + ], + "objectGroups": [] + }, + { + "description": "Delete all noise generators and loose their settings.", + "fullName": "Delete all noise generators", + "functionType": "Action", + "name": "DeleteAll", + "private": true, + "sentence": "Delete all noise generators", + "events": [ + { + "type": "BuiltinCommonInstructions::JsCode", + "inlineCode": "gdjs._shakeObjectExtension.noiseManager.deleteAllGenerators();", + "parameterObjects": "", + "useStrict": true, + "eventsSheetExpanded": false + } + ], + "parameters": [], + "objectGroups": [] + }, + { + "description": "The seed is a number used to generate the random noise. Setting the same seed will result in the same random noise generation. It's for example useful to generate the same world, by saving this seed value and reusing it later to generate again a world.", + "fullName": "Noise seed", + "functionType": "Action", + "name": "SetSeed", + "private": true, + "sentence": "Change the noise seed to _PARAM1_", + "events": [ + { + "type": "BuiltinCommonInstructions::JsCode", + "inlineCode": "gdjs._shakeObjectExtension.noiseManager.setSeed(eventsFunctionContext.getArgument(\"Seed\"));", + "parameterObjects": "", + "useStrict": true, + "eventsSheetExpanded": false + } + ], + "parameters": [ + { + "description": "Seed", + "longDescription": "15 digits numbers maximum", + "name": "Seed", + "type": "expression" + } + ], + "objectGroups": [] + }, + { + "description": "Change the looping period on X used for noise generation. The noise will wrap-around on X.", + "fullName": "Noise looping period on X", + "functionType": "Action", + "name": "SetLoopPeriodX", + "private": true, + "sentence": "Change the looping period on X of _PARAM2_: _PARAM1_", + "events": [ + { + "type": "BuiltinCommonInstructions::JsCode", + "inlineCode": [ + "const name = eventsFunctionContext.getArgument(\"Name\");\r", + "\r", + "gdjs._shakeObjectExtension.noiseManager.getGenerator(name).xLoopPeriod = eventsFunctionContext.getArgument(\"LoopPeriod\");" + ], + "parameterObjects": "", + "useStrict": true, + "eventsSheetExpanded": false + } + ], + "parameters": [ + { + "description": "Looping period on X", + "name": "LoopPeriod", + "type": "expression" + }, + { + "description": "Generator name", + "name": "Name", + "type": "string" + } + ], + "objectGroups": [] + }, + { + "description": "Change the looping period on Y used for noise generation. The noise will wrap-around on Y.", + "fullName": "Noise looping period on Y", + "functionType": "Action", + "name": "SetLoopPeriodY", + "private": true, + "sentence": "Change the looping period on Y of _PARAM2_: _PARAM1_", + "events": [ + { + "type": "BuiltinCommonInstructions::JsCode", + "inlineCode": [ + "const name = eventsFunctionContext.getArgument(\"Name\");\r", + "\r", + "gdjs._shakeObjectExtension.noiseManager.getGenerator(name).yLoopPeriod = eventsFunctionContext.getArgument(\"LoopPeriod\");" + ], + "parameterObjects": "", + "useStrict": true, + "eventsSheetExpanded": true + } + ], + "parameters": [ + { + "description": "Looping period on Y", + "name": "LoopPeriod", + "type": "expression" + }, + { + "description": "Generator name", + "name": "Name", + "type": "string" + } + ], + "objectGroups": [] + }, + { + "description": "Change the base frequency used for noise generation. A lower frequency will zoom in the noise.", + "fullName": "Noise base frequency", + "functionType": "Action", + "name": "SetFrequency", + "private": true, + "sentence": "Change the noise frequency of _PARAM2_: _PARAM1_", + "events": [ + { + "type": "BuiltinCommonInstructions::JsCode", + "inlineCode": [ + "const name = eventsFunctionContext.getArgument(\"Name\");\r", + "\r", + "gdjs._shakeObjectExtension.noiseManager.getGenerator(name).frequency = eventsFunctionContext.getArgument(\"Frequency\");" + ], + "parameterObjects": "", + "useStrict": true, + "eventsSheetExpanded": false + } + ], + "parameters": [ + { + "description": "Frequency", + "name": "Frequency", + "type": "expression" + }, + { + "description": "Generator name", + "name": "Name", + "type": "string" + } + ], + "objectGroups": [] + }, + { + "description": "Change the number of octaves used for noise generation. It can be seen as layers of noise with different zoom.", + "fullName": "Noise octaves", + "functionType": "Action", + "name": "SetOctaves", + "private": true, + "sentence": "Change the number of noise octaves of _PARAM2_: _PARAM1_", + "events": [ + { + "type": "BuiltinCommonInstructions::JsCode", + "inlineCode": [ + "const name = eventsFunctionContext.getArgument(\"Name\");\r", + "\r", + "gdjs._shakeObjectExtension.noiseManager.getGenerator(name).octaves = eventsFunctionContext.getArgument(\"Octaves\");" + ], + "parameterObjects": "", + "useStrict": true, + "eventsSheetExpanded": false + } + ], + "parameters": [ + { + "description": "Octaves", + "name": "Octaves", + "type": "expression" + }, + { + "description": "Generator name", + "name": "Name", + "type": "string" + } + ], + "objectGroups": [] + }, + { + "description": "Change the persistence used for noise generation. At its default value \"0.5\", it halves the noise amplitude at each octave.", + "fullName": "Noise persistence", + "functionType": "Action", + "name": "SetPersistence", + "private": true, + "sentence": "Change the noise persistence of _PARAM2_: _PARAM1_", + "events": [ + { + "type": "BuiltinCommonInstructions::JsCode", + "inlineCode": [ + "const name = eventsFunctionContext.getArgument(\"Name\");\r", + "\r", + "gdjs._shakeObjectExtension.noiseManager.getGenerator(name).persistence = eventsFunctionContext.getArgument(\"Persistence\");" + ], + "parameterObjects": "", + "useStrict": true, + "eventsSheetExpanded": false + } + ], + "parameters": [ + { + "description": "Persistence", + "name": "Persistence", + "type": "expression" + }, + { + "description": "Generator name", + "name": "Name", + "type": "string" + } + ], + "objectGroups": [] + }, + { + "description": "Change the lacunarity used for noise generation. At its default value \"2\", it doubles the frequency at each octave.", + "fullName": "Noise lacunarity", + "functionType": "Action", + "name": "SetLacunarity", + "private": true, + "sentence": "Change the noise lacunarity of _PARAM2_: _PARAM1_", + "events": [ + { + "type": "BuiltinCommonInstructions::JsCode", + "inlineCode": [ + "const name = eventsFunctionContext.getArgument(\"Name\");\r", + "\r", + "gdjs._shakeObjectExtension.noiseManager.getGenerator(name).lacunarity = eventsFunctionContext.getArgument(\"Lacunarity\");" + ], + "parameterObjects": "", + "useStrict": true, + "eventsSheetExpanded": false + } + ], + "parameters": [ + { + "description": "Lacunarity", + "name": "Lacunarity", + "type": "expression" + }, + { + "description": "Generator name", + "name": "Name", + "type": "string" + } + ], + "objectGroups": [] + }, + { + "description": "The seed used for noise generation.", + "fullName": "Noise seed", + "functionType": "Expression", + "name": "Seed", + "private": true, + "sentence": "", + "events": [ + { + "type": "BuiltinCommonInstructions::JsCode", + "inlineCode": "eventsFunctionContext.returnValue = gdjs._shakeObjectExtension.noiseManager.seed;", + "parameterObjects": "", + "useStrict": true, + "eventsSheetExpanded": false + } + ], + "expressionType": { + "type": "expression" + }, + "parameters": [], + "objectGroups": [] + }, + { + "description": "The base frequency used for noise generation.", + "fullName": "Noise base frequency", + "functionType": "Expression", + "name": "Frequency", + "private": true, + "sentence": "", + "events": [ + { + "type": "BuiltinCommonInstructions::JsCode", + "inlineCode": [ + "const name = eventsFunctionContext.getArgument(\"Name\");\r", + "\r", + "eventsFunctionContext.returnValue = gdjs._shakeObjectExtension.noiseManager.getGenerator(name).frequency;" + ], + "parameterObjects": "", + "useStrict": true, + "eventsSheetExpanded": false + } + ], + "expressionType": { + "type": "expression" + }, + "parameters": [ + { + "description": "Generator name", + "name": "Name", + "type": "string" + } + ], + "objectGroups": [] + }, + { + "description": "The number of octaves used for noise generation.", + "fullName": "Noise octaves number", + "functionType": "Expression", + "name": "Octaves", + "private": true, + "sentence": "", + "events": [ + { + "type": "BuiltinCommonInstructions::JsCode", + "inlineCode": [ + "const name = eventsFunctionContext.getArgument(\"Name\");\r", + "\r", + "eventsFunctionContext.returnValue = gdjs._shakeObjectExtension.noiseManager.getGenerator(name).octaves;" + ], + "parameterObjects": "", + "useStrict": true, + "eventsSheetExpanded": false + } + ], + "expressionType": { + "type": "expression" + }, + "parameters": [ + { + "description": "Generator name", + "name": "Name", + "type": "string" + } + ], + "objectGroups": [] + }, + { + "description": "The persistence used for noise generation.", + "fullName": "Noise persistence", + "functionType": "Expression", + "name": "Persistence", + "private": true, + "sentence": "", + "events": [ + { + "type": "BuiltinCommonInstructions::JsCode", + "inlineCode": [ + "const name = eventsFunctionContext.getArgument(\"Name\");\r", + "\r", + "eventsFunctionContext.returnValue = gdjs._shakeObjectExtension.noiseManager.getGenerator(name).persistence;" + ], + "parameterObjects": "", + "useStrict": true, + "eventsSheetExpanded": false + } + ], + "expressionType": { + "type": "expression" + }, + "parameters": [ + { + "description": "Generator name", + "name": "Name", + "type": "string" + } + ], + "objectGroups": [] + }, + { + "description": "The lacunarity used for noise generation.", + "fullName": "Noise lacunarity", + "functionType": "Expression", + "name": "Lacunarity", + "private": true, + "sentence": "", + "events": [ + { + "type": "BuiltinCommonInstructions::JsCode", + "inlineCode": [ + "const name = eventsFunctionContext.getArgument(\"Name\");\r", + "\r", + "eventsFunctionContext.returnValue = gdjs._shakeObjectExtension.noiseManager.getGenerator(name).lacunarity;" + ], + "parameterObjects": "", + "useStrict": true, + "eventsSheetExpanded": false + } + ], + "expressionType": { + "type": "expression" + }, + "parameters": [ + { + "description": "Generator name", + "name": "Name", + "type": "string" + } + ], + "objectGroups": [] + } + ], + "eventsFunctionsFolderStructure": { + "folderName": "__ROOT", + "children": [ + { + "functionName": "onFirstSceneLoaded" + }, + { + "functionName": "DefineHelperClasses" + }, + { + "functionName": "Noise2d" + }, + { + "functionName": "Noise3d" + }, + { + "functionName": "Noise4d" + }, + { + "functionName": "Create" + }, + { + "functionName": "Delete" + }, + { + "functionName": "DeleteAll" + }, + { + "functionName": "SetSeed" + }, + { + "functionName": "SetLoopPeriodX" + }, + { + "functionName": "SetLoopPeriodY" + }, + { + "functionName": "SetFrequency" + }, + { + "functionName": "SetOctaves" + }, + { + "functionName": "SetPersistence" + }, + { + "functionName": "SetLacunarity" + }, + { + "functionName": "Seed" + }, + { + "functionName": "Frequency" + }, + { + "functionName": "Octaves" + }, + { + "functionName": "Persistence" + }, + { + "functionName": "Lacunarity" + } + ] + }, + "eventsBasedBehaviors": [ + { + "description": "Shake 3D objects with translation and rotation.", + "fullName": "3D shake", + "helpPath": "", + "iconUrl": "", + "name": "ShakeModel3D", + "objectType": "", + "previewIconUrl": "", + "quickCustomizationVisibility": "hidden", + "eventsFunctions": [ + { + "fullName": "", + "functionType": "Action", + "name": "onCreated", + "sentence": "", + "events": [ + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [ + { + "type": { + "value": "BooleanVariable" + }, + "parameters": [ + "IsStartingAtCreation", + "True", + "" + ] + } + ], + "actions": [ + { + "type": { + "value": "ShakeObject3D::DefineHelperClasses" + }, + "parameters": [ + "", + "" + ] + }, + { + "type": { + "value": "ShakeObject3D::ShakeModel3D::StartShaking" + }, + "parameters": [ + "Object", + "Behavior", + "0", + "" + ] + } + ] + } + ], + "parameters": [ + { + "description": "Object", + "name": "Object", + "type": "object" + }, + { + "description": "Behavior", + "name": "Behavior", + "supplementaryInformation": "ShakeObject3D::ShakeModel3D", + "type": "behavior" + } + ], + "objectGroups": [] + }, + { + "fullName": "", + "functionType": "Action", + "name": "doStepPostEvents", + "sentence": "", + "events": [ + { + "type": "BuiltinCommonInstructions::Comment", + "color": { + "b": 109, + "g": 230, + "r": 255, + "textB": 0, + "textG": 0, + "textR": 0 + }, + "comment": "Step time counters." + }, + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [], + "actions": [ + { + "type": { + "value": "SetNumberVariable" + }, + "parameters": [ + "Time", + "+", + "TimeDelta()" + ] + } + ] + }, + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [ + { + "type": { + "value": "ShakeObject3D::ShakeModel3D::IsShaking" + }, + "parameters": [ + "Object", + "Behavior", + "" + ] + } + ], + "actions": [ + { + "type": { + "value": "SetNumberVariable" + }, + "parameters": [ + "NoiseTime", + "+", + "TimeDelta() * Frequency" + ] + }, + { + "type": { + "value": "ShakeObject3D::ShakeModel3D::SetSharedPropertyEasingFactor" + }, + "parameters": [ + "Object", + "Behavior", + "=", + "min(Object.Behavior::StartEasingFactor(), Object.Behavior::StopEasingFactor())" + ] + } + ], + "events": [ + { + "type": "BuiltinCommonInstructions::Comment", + "color": { + "b": 109, + "g": 230, + "r": 255, + "textB": 0, + "textG": 0, + "textR": 0 + }, + "comment": "Shake the object.\nSave the object displacement to revert it in onScenePostEvents." + }, + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [ + { + "type": { + "value": "NumberVariable" + }, + "parameters": [ + "TranslationAmplitudeX", + "!=", + "0" + ] + } + ], + "actions": [ + { + "type": { + "value": "SetNumberVariable" + }, + "parameters": [ + "DeltaX", + "=", + "ShakeObject3D::Noise2d(\"\", NoiseTime, 1000) * TranslationAmplitudeX * EasingFactor" + ] + }, + { + "type": { + "value": "SetX" + }, + "parameters": [ + "Object", + "+", + "DeltaX" + ] + } + ] + }, + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [ + { + "type": { + "value": "NumberVariable" + }, + "parameters": [ + "TranslationAmplitudeY", + "!=", + "0" + ] + } + ], + "actions": [ + { + "type": { + "value": "SetNumberVariable" + }, + "parameters": [ + "DeltaY", + "=", + "ShakeObject3D::Noise2d(\"\", NoiseTime, 2000) * TranslationAmplitudeY * EasingFactor" + ] + }, + { + "type": { + "value": "SetY" + }, + "parameters": [ + "Object", + "+", + "DeltaY" + ] + } + ] + }, + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [ + { + "type": { + "value": "NumberVariable" + }, + "parameters": [ + "TranslationAmplitudeZ", + "!=", + "0" + ] + } + ], + "actions": [ + { + "type": { + "value": "SetNumberVariable" + }, + "parameters": [ + "DeltaZ", + "=", + "ShakeObject3D::Noise2d(\"\", NoiseTime, 3000) * TranslationAmplitudeZ * EasingFactor" + ] + }, + { + "type": { + "value": "Scene3D::Base3DBehavior::SetZ" + }, + "parameters": [ + "Object", + "Object3D", + "+", + "DeltaZ" + ] + } + ] + }, + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [ + { + "type": { + "value": "NumberVariable" + }, + "parameters": [ + "RotationAmplitudeX", + "!=", + "0" + ] + } + ], + "actions": [ + { + "type": { + "value": "SetNumberVariable" + }, + "parameters": [ + "DeltaAngleX", + "=", + "ShakeObject3D::Noise2d(\"\", NoiseTime, 4000) * RotationAmplitudeX * EasingFactor" + ] + }, + { + "type": { + "value": "Scene3D::Base3DBehavior::SetRotationX" + }, + "parameters": [ + "Object", + "Object3D", + "+", + "DeltaAngleX" + ] + } + ] + }, + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [ + { + "type": { + "value": "NumberVariable" + }, + "parameters": [ + "RotationAmplitudeY", + "!=", + "0" + ] + } + ], + "actions": [ + { + "type": { + "value": "SetNumberVariable" + }, + "parameters": [ + "DeltaAngleY", + "=", + "ShakeObject3D::Noise2d(\"\", NoiseTime, 5000) * RotationAmplitudeY * EasingFactor" + ] + }, + { + "type": { + "value": "Scene3D::Base3DBehavior::SetRotationY" + }, + "parameters": [ + "Object", + "Object3D", + "+", + "DeltaAngleY" + ] + } + ] + }, + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [ + { + "type": { + "value": "NumberVariable" + }, + "parameters": [ + "RotationAmplitudeZ", + "!=", + "0" + ] + } + ], + "actions": [ + { + "type": { + "value": "SetNumberVariable" + }, + "parameters": [ + "DeltaAngleZ", + "=", + "ShakeObject3D::Noise2d(\"\", NoiseTime, 6000) * RotationAmplitudeZ * EasingFactor" + ] + }, + { + "type": { + "value": "SetAngle" + }, + "parameters": [ + "Object", + "+", + "DeltaAngleZ" + ] + } + ] + } + ] + } + ], + "parameters": [ + { + "description": "Object", + "name": "Object", + "type": "object" + }, + { + "description": "Behavior", + "name": "Behavior", + "supplementaryInformation": "ShakeObject3D::ShakeModel3D", + "type": "behavior" + } + ], + "objectGroups": [] + }, + { + "fullName": "", + "functionType": "Action", + "name": "doStepPreEvents", + "sentence": "", + "events": [ + { + "type": "BuiltinCommonInstructions::Comment", + "color": { + "b": 109, + "g": 230, + "r": 255, + "textB": 0, + "textG": 0, + "textR": 0 + }, + "comment": "Revert the shaking." + }, + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [ + { + "type": { + "value": "ShakeObject3D::ShakeModel3D::IsShaking" + }, + "parameters": [ + "Object", + "Behavior", + "" + ] + } + ], + "actions": [], + "events": [ + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [ + { + "type": { + "value": "NumberVariable" + }, + "parameters": [ + "TranslationAmplitudeX", + "!=", + "0" + ] + } + ], + "actions": [ + { + "type": { + "value": "SetX" + }, + "parameters": [ + "Object", + "-", + "DeltaX" + ] + } + ] + }, + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [ + { + "type": { + "value": "NumberVariable" + }, + "parameters": [ + "TranslationAmplitudeY", + "!=", + "0" + ] + } + ], + "actions": [ + { + "type": { + "value": "SetY" + }, + "parameters": [ + "Object", + "-", + "DeltaY" + ] + } + ] + }, + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [ + { + "type": { + "value": "NumberVariable" + }, + "parameters": [ + "TranslationAmplitudeZ", + "!=", + "0" + ] + } + ], + "actions": [ + { + "type": { + "value": "Scene3D::Base3DBehavior::SetZ" + }, + "parameters": [ + "Object", + "Object3D", + "-", + "DeltaZ" + ] + } + ] + }, + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [ + { + "type": { + "value": "NumberVariable" + }, + "parameters": [ + "RotationAmplitudeX", + "!=", + "0" + ] + } + ], + "actions": [ + { + "type": { + "value": "Scene3D::Base3DBehavior::SetRotationX" + }, + "parameters": [ + "Object", + "Object3D", + "-", + "DeltaAngleX" + ] + } + ] + }, + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [ + { + "type": { + "value": "NumberVariable" + }, + "parameters": [ + "RotationAmplitudeY", + "!=", + "0" + ] + } + ], + "actions": [ + { + "type": { + "value": "Scene3D::Base3DBehavior::SetRotationY" + }, + "parameters": [ + "Object", + "Object3D", + "-", + "DeltaAngleY" + ] + } + ] + }, + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [ + { + "type": { + "value": "NumberVariable" + }, + "parameters": [ + "RotationAmplitudeZ", + "!=", + "0" + ] + } + ], + "actions": [ + { + "type": { + "value": "SetAngle" + }, + "parameters": [ + "Object", + "-", + "DeltaAngleZ" + ] + } + ] + } + ] + } + ], + "parameters": [ + { + "description": "Object", + "name": "Object", + "type": "object" + }, + { + "description": "Behavior", + "name": "Behavior", + "supplementaryInformation": "ShakeObject3D::ShakeModel3D", + "type": "behavior" + } + ], + "objectGroups": [] + }, + { + "description": "Shake the object with a linear easing at the start and the end.", + "fullName": "Shake", + "functionType": "Action", + "name": "Shake", + "sentence": "Shake _PARAM0_ for _PARAM2_ seconds with _PARAM3_ seconds of easing to start and _PARAM4_ seconds to stop", + "events": [ + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [], + "actions": [ + { + "type": { + "value": "SetNumberVariable" + }, + "parameters": [ + "Time", + "=", + "0" + ] + }, + { + "type": { + "value": "SetNumberVariable" + }, + "parameters": [ + "NoiseTime", + "=", + "TimeFromStart() * Frequency" + ] + }, + { + "type": { + "value": "SetNumberVariable" + }, + "parameters": [ + "Duration", + "=", + "NewDuration" + ] + }, + { + "type": { + "value": "SetNumberVariable" + }, + "parameters": [ + "StartEasingDuration", + "=", + "StartEaseDuration" + ] + }, + { + "type": { + "value": "SetNumberVariable" + }, + "parameters": [ + "StopEasingDuration", + "=", + "StopEaseDuration" + ] + } + ] + }, + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [ + { + "type": { + "value": "NumberVariable" + }, + "parameters": [ + "NewDuration", + "<", + "StartEaseDuration + StopEaseDuration" + ] + } + ], + "actions": [ + { + "type": { + "value": "SetNumberVariable" + }, + "parameters": [ + "StartEasingDuration", + "=", + "StartEaseDuration * NewDuration / (StartEaseDuration + StopEaseDuration)" + ] + }, + { + "type": { + "value": "SetNumberVariable" + }, + "parameters": [ + "StopEasingDuration", + "=", + "StopEaseDuration * NewDuration / (StartEaseDuration + StopEaseDuration)" + ] + } + ] + } + ], + "parameters": [ + { + "description": "Object", + "name": "Object", + "type": "object" + }, + { + "description": "Behavior", + "name": "Behavior", + "supplementaryInformation": "ShakeObject3D::ShakeModel3D", + "type": "behavior" + }, + { + "description": "Duration (in seconds)", + "name": "NewDuration", + "type": "expression" + }, + { + "description": "Ease duration to start (in seconds)", + "name": "StartEaseDuration", + "type": "expression" + }, + { + "description": "Ease duration to stop (in seconds)", + "name": "StopEaseDuration", + "type": "expression" + } + ], + "objectGroups": [] + }, + { + "description": "Shake the object with a linear easing at the start and keep shaking until the stop action is used.", + "fullName": "Start shaking", + "functionType": "Action", + "name": "StartShaking", + "sentence": "Start shaking _PARAM0_ with _PARAM2_ seconds of easing", + "events": [ + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [], + "actions": [ + { + "type": { + "value": "SetNumberVariable" + }, + "parameters": [ + "Time", + "=", + "0" + ] + }, + { + "type": { + "value": "SetNumberVariable" + }, + "parameters": [ + "NoiseTime", + "=", + "TimeFromStart() * Frequency" + ] + }, + { + "type": { + "value": "SetNumberVariable" + }, + "parameters": [ + "Duration", + "=", + "1234567890" + ] + }, + { + "type": { + "value": "SetNumberVariable" + }, + "parameters": [ + "StartEasingDuration", + "=", + "EaseDuration" + ] + } + ] + } + ], + "parameters": [ + { + "description": "Object", + "name": "Object", + "type": "object" + }, + { + "description": "Behavior", + "name": "Behavior", + "supplementaryInformation": "ShakeObject3D::ShakeModel3D", + "type": "behavior" + }, + { + "description": "Ease duration (in seconds)", + "name": "EaseDuration", + "type": "expression" + } + ], + "objectGroups": [] + }, + { + "description": "Stop shaking the object with a linear easing.", + "fullName": "Stop shaking", + "functionType": "Action", + "name": "StopShaking", + "sentence": "Stop shaking _PARAM0_ with _PARAM2_ seconds of easing", + "events": [ + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [ + { + "type": { + "value": "ShakeObject3D::ShakeModel3D::IsShaking" + }, + "parameters": [ + "Object", + "Behavior", + "" + ] + } + ], + "actions": [ + { + "type": { + "value": "SetNumberVariable" + }, + "parameters": [ + "Time", + "=", + "0" + ] + }, + { + "type": { + "value": "SetNumberVariable" + }, + "parameters": [ + "Duration", + "=", + "EaseDuration" + ] + }, + { + "type": { + "value": "SetNumberVariable" + }, + "parameters": [ + "StopEasingDuration", + "=", + "EaseDuration" + ] + } + ] + } + ], + "parameters": [ + { + "description": "Object", + "name": "Object", + "type": "object" + }, + { + "description": "Behavior", + "name": "Behavior", + "supplementaryInformation": "ShakeObject3D::ShakeModel3D", + "type": "behavior" + }, + { + "description": "Ease duration (in seconds)", + "name": "EaseDuration", + "type": "expression" + } + ], + "objectGroups": [] + }, + { + "description": "Check if the object is shaking.", + "fullName": "Is shaking", + "functionType": "Condition", + "name": "IsShaking", + "sentence": "_PARAM0_ is shaking", + "events": [ + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [ + { + "type": { + "value": "NumberVariable" + }, + "parameters": [ + "Time", + "<", + "Duration" + ] + }, + { + "type": { + "value": "NumberVariable" + }, + "parameters": [ + "Time", + ">", + "0" + ] + } + ], + "actions": [ + { + "type": { + "value": "SetReturnBoolean" + }, + "parameters": [ + "True" + ] + } + ] + } + ], + "parameters": [ + { + "description": "Object", + "name": "Object", + "type": "object" + }, + { + "description": "Behavior", + "name": "Behavior", + "supplementaryInformation": "ShakeObject3D::ShakeModel3D", + "type": "behavior" + } + ], + "objectGroups": [] + }, + { + "description": "Check if the object is stopping to shake.", + "fullName": "Is stopping to shake", + "functionType": "Condition", + "name": "IsStopping", + "sentence": "_PARAM0_ is stopping to shake", + "events": [ + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [ + { + "type": { + "value": "ShakeObject3D::ShakeModel3D::IsShaking" + }, + "parameters": [ + "Object", + "Behavior", + "" + ] + }, + { + "type": { + "value": "ShakeObject3D::ShakeModel3D::StopEasingFactor" + }, + "parameters": [ + "Object", + "Behavior", + "<", + "1", + "" + ] + } + ], + "actions": [ + { + "type": { + "value": "SetReturnBoolean" + }, + "parameters": [ + "True" + ] + } + ] + } + ], + "parameters": [ + { + "description": "Object", + "name": "Object", + "type": "object" + }, + { + "description": "Behavior", + "name": "Behavior", + "supplementaryInformation": "ShakeObject3D::ShakeModel3D", + "type": "behavior" + } + ], + "objectGroups": [] + }, + { + "description": "the shaking frequency of the object.", + "fullName": "Shaking frequency", + "functionType": "ExpressionAndCondition", + "group": "ShakeObject3D configuration", + "name": "Frequency", + "sentence": "the shaking frequency", + "events": [ + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [], + "actions": [ + { + "type": { + "value": "SetReturnNumber" + }, + "parameters": [ + "Frequency" + ] + } + ] + } + ], + "expressionType": { + "type": "expression" + }, + "parameters": [ + { + "description": "Object", + "name": "Object", + "type": "object" + }, + { + "description": "Behavior", + "name": "Behavior", + "supplementaryInformation": "ShakeObject3D::ShakeModel3D", + "type": "behavior" + } + ], + "objectGroups": [] + }, + { + "fullName": "", + "functionType": "ActionWithOperator", + "getterName": "Frequency", + "group": "ShakeObject3D configuration", + "name": "SetFrequency", + "sentence": "", + "events": [ + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [], + "actions": [ + { + "type": { + "value": "SetNumberVariable" + }, + "parameters": [ + "Frequency", + "=", + "Value" + ] + } + ] + } + ], + "parameters": [ + { + "description": "Object", + "name": "Object", + "type": "object" + }, + { + "description": "Behavior", + "name": "Behavior", + "supplementaryInformation": "ShakeObject3D::ShakeModel3D", + "type": "behavior" + } + ], + "objectGroups": [] + }, + { + "description": "Return the easing factor according to start properties.", + "fullName": "Start easing factor", + "functionType": "Expression", + "name": "StartEasingFactor", + "private": true, + "sentence": "", + "events": [ + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [ + { + "type": { + "value": "NumberVariable" + }, + "parameters": [ + "StartEasingDuration", + "<=", + "0" + ] + } + ], + "actions": [ + { + "type": { + "value": "SetReturnNumber" + }, + "parameters": [ + "1" + ] + } + ] + }, + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [ + { + "type": { + "value": "NumberVariable" + }, + "parameters": [ + "StartEasingDuration", + ">", + "0" + ] + } + ], + "actions": [ + { + "type": { + "value": "SetReturnNumber" + }, + "parameters": [ + "clamp(Time / StartEasingDuration, 0, 1)" + ] + } + ] + } + ], + "expressionType": { + "type": "expression" + }, + "parameters": [ + { + "description": "Object", + "name": "Object", + "type": "object" + }, + { + "description": "Behavior", + "name": "Behavior", + "supplementaryInformation": "ShakeObject3D::ShakeModel3D", + "type": "behavior" + } + ], + "objectGroups": [] + }, + { + "description": "Return the easing factor according to stop properties.", + "fullName": "Stop easing factor", + "functionType": "ExpressionAndCondition", + "name": "StopEasingFactor", + "private": true, + "sentence": "stop easing factor", + "events": [ + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [ + { + "type": { + "value": "NumberVariable" + }, + "parameters": [ + "StopEasingDuration", + "<=", + "0" + ] + } + ], + "actions": [], + "events": [ + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [ + { + "type": { + "value": "NumberVariable" + }, + "parameters": [ + "Time", + "<", + "Duration" + ] + } + ], + "actions": [ + { + "type": { + "value": "SetReturnNumber" + }, + "parameters": [ + "1" + ] + } + ] + }, + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [ + { + "type": { + "value": "NumberVariable" + }, + "parameters": [ + "Time", + ">=", + "Duration" + ] + } + ], + "actions": [ + { + "type": { + "value": "SetReturnNumber" + }, + "parameters": [ + "0" + ] + } + ] + } + ] + }, + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [ + { + "type": { + "value": "NumberVariable" + }, + "parameters": [ + "StopEasingDuration", + ">", + "0" + ] + } + ], + "actions": [ + { + "type": { + "value": "SetReturnNumber" + }, + "parameters": [ + "clamp((Duration - Time) / StopEasingDuration, 0, 1)" + ] + } + ] + } + ], + "expressionType": { + "type": "expression" + }, + "parameters": [ + { + "description": "Object", + "name": "Object", + "type": "object" + }, + { + "description": "Behavior", + "name": "Behavior", + "supplementaryInformation": "ShakeObject3D::ShakeModel3D", + "type": "behavior" + } + ], + "objectGroups": [] + } + ], + "eventsFunctionsFolderStructure": { + "folderName": "__ROOT", + "children": [ + { + "functionName": "onCreated" + }, + { + "functionName": "doStepPostEvents" + }, + { + "functionName": "doStepPreEvents" + }, + { + "functionName": "Shake" + }, + { + "functionName": "StartShaking" + }, + { + "functionName": "StopShaking" + }, + { + "functionName": "IsShaking" + }, + { + "functionName": "IsStopping" + }, + { + "folderName": "ShakeObject3D configuration", + "children": [ + { + "functionName": "Frequency" + }, + { + "functionName": "SetFrequency" + } + ] + }, + { + "functionName": "StartEasingFactor" + }, + { + "functionName": "StopEasingFactor" + } + ] + }, + "propertyDescriptors": [ + { + "value": "0", + "type": "Number", + "unit": "Pixel", + "label": "Translation amplitude on X axis", + "name": "TranslationAmplitudeX" + }, + { + "value": "0", + "type": "Number", + "unit": "Pixel", + "label": "Translation amplitude on Y axis", + "name": "TranslationAmplitudeY" + }, + { + "value": "0", + "type": "Number", + "unit": "Pixel", + "label": "Translation amplitude on Z axis", + "name": "TranslationAmplitudeZ" + }, + { + "value": "5", + "type": "Number", + "unit": "DegreeAngle", + "label": "Rotation amplitude around X axis", + "name": "RotationAmplitudeX" + }, + { + "value": "5", + "type": "Number", + "unit": "DegreeAngle", + "label": "Rotation amplitude around Y axis", + "name": "RotationAmplitudeY" + }, + { + "value": "5", + "type": "Number", + "unit": "DegreeAngle", + "label": "Rotation amplitude around Z axis", + "name": "RotationAmplitudeZ" + }, + { + "value": "", + "type": "Number", + "label": "", + "hidden": true, + "name": "Time" + }, + { + "value": "", + "type": "Number", + "label": "", + "hidden": true, + "name": "Duration" + }, + { + "value": "", + "type": "Number", + "label": "", + "hidden": true, + "name": "StartEasingDuration" + }, + { + "value": "", + "type": "Number", + "label": "", + "hidden": true, + "name": "StopEasingDuration" + }, + { + "value": "", + "type": "Number", + "label": "Frequency", + "name": "Frequency" + }, + { + "value": "", + "type": "Number", + "label": "", + "hidden": true, + "name": "DeltaX" + }, + { + "value": "", + "type": "Number", + "label": "", + "hidden": true, + "name": "DeltaY" + }, + { + "value": "", + "type": "Number", + "label": "", + "hidden": true, + "name": "DeltaZ" + }, + { + "value": "", + "type": "Number", + "label": "", + "hidden": true, + "name": "DeltaAngleX" + }, + { + "value": "", + "type": "Number", + "label": "", + "hidden": true, + "name": "DeltaAngleY" + }, + { + "value": "", + "type": "Number", + "label": "", + "hidden": true, + "name": "DeltaAngleZ" + }, + { + "value": "", + "type": "Number", + "label": "", + "hidden": true, + "name": "NoiseTime" + }, + { + "value": "", + "type": "Behavior", + "label": "3D capability", + "extraInformation": [ + "Scene3D::Base3DBehavior" + ], + "choices": [], + "name": "Object3D" + }, + { + "value": "", + "type": "Boolean", + "label": "Start to shake at the object creation", + "name": "IsStartingAtCreation" + } + ], + "propertiesFolderStructure": { + "folderName": "__ROOT", + "children": [ + { + "propertyName": "TranslationAmplitudeX" + }, + { + "propertyName": "TranslationAmplitudeY" + }, + { + "propertyName": "TranslationAmplitudeZ" + }, + { + "propertyName": "RotationAmplitudeX" + }, + { + "propertyName": "RotationAmplitudeY" + }, + { + "propertyName": "RotationAmplitudeZ" + }, + { + "propertyName": "Time" + }, + { + "propertyName": "Duration" + }, + { + "propertyName": "StartEasingDuration" + }, + { + "propertyName": "StopEasingDuration" + }, + { + "propertyName": "Frequency" + }, + { + "propertyName": "DeltaX" + }, + { + "propertyName": "DeltaY" + }, + { + "propertyName": "DeltaZ" + }, + { + "propertyName": "DeltaAngleX" + }, + { + "propertyName": "DeltaAngleY" + }, + { + "propertyName": "DeltaAngleZ" + }, + { + "propertyName": "NoiseTime" + }, + { + "propertyName": "Object3D" + }, + { + "propertyName": "IsStartingAtCreation" + } + ] + }, + "sharedPropertyDescriptors": [ + { + "value": "", + "type": "Number", + "label": "", + "hidden": true, + "name": "EasingFactor" + } + ], + "sharedPropertiesFolderStructure": { + "folderName": "__ROOT", + "children": [ + { + "propertyName": "EasingFactor" + } + ] + } + } + ], + "eventsBasedObjects": [] } ], "externalLayouts": [] diff --git a/examples/starting-beatemup/assets/Collision.wav b/examples/starting-beatemup/assets/Collision.wav new file mode 100644 index 0000000000000000000000000000000000000000..5b89dda9865db53229ee501b111b0612758f968b GIT binary patch literal 18870 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 delta 928 zcmV;R17G~79jgbB8Gi!+006uZmmdHC02@$DR7Ff_aWyqHHa0djH8nOiHZ3hJHa0dm zI5|R^?&H|1(F!EEFDZlRnwi_ z{vfi zj=HzjIAJ?^*aIR1yr3o5f0`Hd%5^<6F6Up zP~Fjl;*KVq#|bI9VKHMl8{r`)iPZ%{-xG$jf4=?q~^F0)IA24NSmi2{R2agHyOAVdeoQ zxPbQ(79!wkIWZ#NI!%xsUi&yP0Snv1t6i5;0fp`1zdpu#*d3k2|9q@mKxK#E_j(Kt z!LJ|`GN7)T^niore1Z;j$NmwypId1iKp=wpMU(OX1$5-Pn-+2DQ3 z3U8HfoorJ0VFQ$enSS6!7WRpkSLewMoFS+@5$ws+?STP?b}+=r0-O&JIbb}yTqpDq z_kD-lXD%rF#snxfL8d2)U`M4yN4i*I^UWcQjl?nfK%C*3a$s;&f%6R|JUAMx!11?+{;EmT?0T5AdBGkQj7xl&{aQ(}c)V~^X*e%3^Jziw;PzI z-Q)Pxrd*^ diff --git a/examples/starting-beatemup/assets/Flat dark joystick thumb.png b/examples/starting-beatemup/assets/Flat dark joystick thumb.png index b30b8173b5a3aa63ac6f397193562b17ece641a5..8acf179f8660be36ccea1198105da35c07154fd7 100644 GIT binary patch literal 2939 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 AD*ylh literal 1432 zcmV;J1!ww+P)C00015P)t-s0RR9D z3=A0=85$ZI4Gj$l2nQe@8%$|&2?+`N$XXd084?l_xspH-5D*y|85I>3XHX!df;?(f zEAkBh8yg#}2>`zk3gZp~)er}S7!{uu6Kfe4KpGlV85l)1=x6`{026dlPE%th!tOs) z-T)G0nQ&rR&#k1LWlx?-fZvz_yBwW9Iuo7 zoJUbPSQO=FA%7KsrE$5iIOo3xz|wNnBK|7?@?36O#KHqGJMd*)DPxMxj8RoR4FrS+ zV9DUX2Y~><`&(ak{1>|I6H*X>UVx_9HARyEXacBxfTAHu zfCtEN0#GGD1IQRDz$iiL9YE|)!AA)a?*M%bP^0`8x*r4P8z4fDKvCQXL4XFpB%#M- z1R(&B{)c@00r&s}0nqORh~GU8DT$~Jk%+$Wso3bqC1`?`<%0w_&jOGDFcKKzzJ#_pO^4RR%jd5t^&lCbm!PfGW;)WUYKVJNIzU-f z8wj2Isv;#Jr~bgLs#XyO_f=R0hHP9{)|B~y&_+$a)*%}dnH_-#Q=QF!X^CATAPR8R z3x^9JeI-f&L;<>@=mmkY`?4$qz$m~&ysbR`R1gv*0Ga?c?#v2;G7nqR>`$12rXS%G6y#W-3TO#lQz>WYg z4*=XP4~?0K9|q{YoOH`WV8nN<6D5%1)zNZ=tLj^Nbz$aWJmHqcE8WYm&dT@AHb<1vo+b{ebxz90cQRoc3%yX z)9G@#6JP=O%s)i#7194+y>%4Zaj*iw=KdjduZsWsnrxJUp#a1JAd6jhM#KsKIdQ1}dB2jFy>hctJJfzHk&L4!cB-?%69N4LhDX2C!(epbJWFn#5p zTrO+3kuG__j>2@X;r8n7`E5FCk*(fDIAy}0bvc<%a@1-i9*vrgS(M55#x0oBTCj#t zw)M4tUpAUj*Salt-DB5+#cI5TuwmQo*Vki2J+O$zDBkuSM`xhuC@cLdd3WVTHhWL_-@qG%x@y?wNJjxz}izg7#dG1`x mUpw|IM`QM{{2Y9C1^5dWH|Vg&0tyBoK_eB2su5 z)<7VlG(kal&s5nY1Y@9;NGt+MvtZe@tYwjq1d@i+zy0lU_5uE{mq1l$}fGUQi zwg|n54i1FQKrKGCH9dQrcUODy8Twd2#FgLYrK#y*(KdZcW8S*7nTut`U$PoP;88t^ z(3&P&f?iGRwGK$U-i(0*hnjytA9FkM!slH#Os3pCq>oE@3Xvhy!#X0(YP<26=)hgO zKA%fm`CbW$Kw#rSQq*maLPyL6(%60Bu{2BO5cj?z*1aH=9rOcx7OwN{Me^ok1B&gX}IZR1U@(T3@<-s zdYJ@ab&W)a5m?!lE4{B%vggY$UKN=>68~iJ;0a~^?L59PyMYSW zkT0Z3QTca1W2#V;F=|mgVbP@XhnR-A9U^9Ffxy8%7;tv;`epYR4yfNQ4HQk0UkPon zdx|nHlTn9SOd)xPIYe_i)$p&u@Ep1?ExO|ip>>jbqdi(B^wJTIG!HiIB^LT(*&S3r zqk$^q8`TP^i2j3-1CmevHKRHoEnisQD6}c>*Ms@!2>+Y#%fTnDEzycBt4|kGcX`eV z9dw)~0*2Hf1WwN(Axx%UJ+S>^sw(7~;3UA@>*jnoT6WGhNAZ}YE;~!-eHwG(_yh@^KDg>4Oh+`|!jXG5tFuj$S<~v)JxmcaeuIt}tLGn-M+JqJ;%n^7 zaErCWtBR#-(J*3WsN~Plm4VqAyB3NJCAP{s*;ukMfoXXcrZ3cUlneN`cS$F?EA()* zy`zWddWvpN;H|Aj)sX*VAGCd?pG}tEj=D2QSe0&$VAL?tt?Z<8cFAdfpL=OCXBnz? z61Njf=bc*&T<#k~wXO%9)u1VMI`JcANauwW&+Z@9aP6l3UDOB9QXo8R0j;rHxv`$; zH5=JShE+GRcV8z_3GM~HHiwkY*;orR+_I!WDB_rnl&Ly-l*B+kBHo9qbT00f708vDJ+SR@M zhK@PAlAwfY6({-W)~_0T+h}9B`18xb2vMj>+Ey>eI6iWbL1M2w*g!uh*SU8IiY{`GBi ze>m|S%9RqsZ+~g&mpU%Bxn1!~DUr}xk(+SEi>zXnloA)Vt@Mc}3dYFJoG%Zp$|4!7 zn!Yn+;%vkq(mmJyW!sgBwf?CiW0~w zJGS|0M7+1CaI?-c1FqDfyc%E2E@R+?)5YrO16z;-00ewAa2Nm`pbJdE#{~cOx~ONM z3cIbG7S6984Nrz(+SM;So;WdFEuxOU3-IU;mo!z6nz6R^KNHrL7W{`*QQvdF<*O7| z=ct{9JqYf8G*_z!qlVTolM)mO;>0i4j2DVU&9k;zW!Oc`bhy{u^(eHszM-b{p~2~^ yaw57DyBl`+)embCNXDGG=Pb>fn0c#yPy^P}>Mk5&n-yzW1JHpX$C{7CQ~w6AS|^$S literal 0 HcmV?d00001 diff --git a/examples/starting-beatemup/assets/StartingObstacle.png b/examples/starting-beatemup/assets/StartingObstacle.png index 5381d12c6aeb8d0d7e5a6228c5d453d61329088e..a963c15fb01bfb514613d3a8beb2763128eef1d2 100644 GIT binary patch literal 429 zcmeAS@N?(olHy`uVBq!ia0vp^4Is?H1|$#LC7uRSY)RhkE)4%caKYZ?lYt_f1s;*b z3=G`DAk4@xYmNj^kiEpy*OmPhyBLqP?5poKlNcBntvp>ELn>~)y{pLA;vnMUn0BaF zK-+>t?5wRI$GIa~LOZxrjiXRS`Bb;!0SM0v#nEKNbw3DuFKT*v91fH&bF6*2UngIGpYBc}= literal 309 zcmeAS@N?(olHy`uVBq!ia0vp^4j|0I3?%1nZ+ru!I0Jk_Ts_>~flL(@m2E}J(?Nm- z9+AZi40_5S%viD1z6>aM$9eYV}(4aQez zzw-UMtSWeK=RbpsyF2H}>&%l|Gfy^Wp3Iwh(k%0&T;@r(%#%1GC$AzWmm(*-L{8?3 zoV19XREV796gi12`{Ygb$(HPs-m*{f%RV`VeKHFBq#X81B-qI}*vT%ilRjW4`M^$2 zz)r@%PAb4oqM%NGLY?%6I@t?#k{|xrN&L8-@MwMW#Qi*J|Li4x-A{P6zj@<-o@f7L zIR4oN{K;1M`P|`i-lB5hy{qqqzF$>-b@!{kjEMrgmy0(&0eX&s!PC{xWt~$(69B{# Ba>)Py diff --git a/examples/starting-beatemup/assets/StartingPlayer.png b/examples/starting-beatemup/assets/StartingPlayer.png index f65a386ff184fb1300e5eb73d710331f963348b6..f1898dc49f0bb12b9d5024bf20a7ccbab7d86152 100644 GIT binary patch literal 5199 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 literal 1433 zcmV;K1!nq*P)C00012P)t-s0000Q z8X5}=3mO_44Gj$k2L~D&8cb<%`^Z`f2?woPVmq<;(qXC?E^c#XwL;_&i z{hsMJtB^(l&SRn_ZDK69CNsavz|%l1zXHNO1p8oB$1gNkaF@sEPrA z)UWgL8{iEP1)zT=K>6x0%rV74%tUm;r(!iPmx!?$wXfWah}-e`|5uQ_z9f5jQHnLe zHCkD&O;7|j00{tN0!8dgXw9h(gr@{@pwxq8fTcN;&UDnvj_3hi0G$J2Qr~r?B;++8 zD7wyypw%w`X)FMpjfc*NR5yrT$@RZO%mGC`ufW*di=1ThkFFq2iGXN;vG1NrKN3v< zL<3Akk#wt(04WA&aRBHn)KLKN{VAZ2N_q+a zepm|VIdMah0_y~Tb%BuvFk+nmur45J021p2fXxCu4WP$n0zmGBL>IshssLGw3{-#t zS(QK>AH;hh1U{?Scwrt0AR!D%H)T;02*4u2bmrm^fZzb9BLF9W9R%1>e;i;30d~}f0Ct8-9O%T`tv) z01Lo3`XTqPh`v8(nJ6l!A6Nj`RzIZvRq?;iRTfh)6hQU>P?x@)QSrHzuP+RM0I-=r zc~k@c9`#=Y0)YJj&?YEoBbwsT0E;6q1ONxS39bZ!{`%knYbbC(0MIW~7wA>c2goJ_ zeq!JVKwY*iep1)9H~sg@u>i~h0Oy6^ibzQ29ry3;PdCE6qCgWYuhs7&tZ(@zmy7K& z!Y2=yRah@J6rKC@T%Vijs}{BOA(>YuyzR?Ld%Ij=@ke#LX3-`m$0Kl=&|!iNLR$>u zd|bAOl;cpeyXvjG5$x{9E(C|T)!X-DL_P4}jd|(TUgl?@=|@yV7Bl2lJ0$t(*|#Va z(H_AR6Q}8-S8|i-ve&78{tee%FlSZFA*gVVz90*yJ`-M@5v#(eub6jl$O2d)V~4uq zqX-#0JRcC;n<3*O%#n>314cXH_+BsyM8=tj@g^1H`xF_+EBCtaC}s?v{Q}7*&68^J nXV-qkB8-_oKHe9fJpuj$>Tsvj<3ki&00000NkvXXu0mjf$!}Tv diff --git a/examples/starting-beatemup/assets/StartingTiledGround.png b/examples/starting-beatemup/assets/StartingTiledGround.png deleted file mode 100644 index e477a6364e3d04944ee18ebfc094b00a53cbda96..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1538 zcmai!doS*^*dBwOV=2cr%nD~GW`tAF;Md(S=h-tW2he(&dV?vJS-d<5{-_2tiYkg4hlfh zJlu|fRFF)>7`5Px9TmovYgH8u5&mu5Q1L<)m}9r2Ifx0m``w<#6Z(i2feUbTJN(>{A zJz;qz>D^VR?_hb4q?N_-=)2ms;_K6Q8c0A?wyt2_Fmv5C)$Z)UB2u|U==Z0ROew-3 zSl;vG>5Eu5xN}7`^>%naQMt!VxH!KX9Y1TnFtd*;FMC-3&%ir&jEa#~+DQ*qvGIc> z`@629$(AV{B36r+PVYjWE=@T{*7Xf@)~Od%&*g^zBc}Bz(!>-4T|Rev z=Uy+70pe1O_;q z^3|oN<<0$KRyk&n_Pzss>(mXlt`E3ENZs+K6X`tQ`1%P8Yi@Zd_O*xY#$aDxy>WL3 z&yZwxybFgynMln#lJN3vX-enf>mU~S#N+gp9%~VP?@VGHSIDd3x{l%F(RNMZ**M_; zqZ)_TYCM6h-rb4I4Tn#vRZG%%Iy8UyB*vDQ6Z_vh{8q>7)_?>9gY$K+GSsxUueJC{<>{q#9c5 z-i%jds}7~DnmeOZ0>+RN<{f0E&SmuH)G#Oj)IMypVN*`$DD*pr`KaHp7GuRJi6!LT z2Ut$Rt_%1i9}re_hp1wR(N_qXn>Z!1XWk%zOrI}!}87Tk4prK@+bZO6ygS43>trAb#8<3n_lXrmKU zY5w!$tp^w}Xl=xvggJ$E2bg%eXsh+c00@-AH|XU#^N8CSkZ-+;i?1zfY{Y~z(l3o2=?k`1c?bu~bv z;Usbz#UI8+Y6q8%oj#2Zq-9QNHHcctxU)6gGs7rY?MmXs2b&N^OdhtfXp#h`B0Bik zW|RKQ0;?;(iutMBTA5X9dEk;pFszmixG`{?0J~5r@ix^*52|3=O@x-c!+YPvHZ+w- zcKf9!_@3_@xpz=1CuSFienEXha{ zQFy->CDAc$+^)(mBf{8ZN^pCV{Mb^(k*30@8kc7s7BU%>f3}K(S~tqVWm@7mJ?b=g zIl$j^Fa`*wB-!aLsyVI$JmW+=WG5U2afyze^j7>SvIL72%)DSbheO_tRG=X_xY<9n H4M_SIJ&?i< diff --git a/examples/starting-beatemup/assets/Target round button.png b/examples/starting-beatemup/assets/Target round button.png index b64abc31b967aa6ba32dbadeb28994865aef26a1..e615798805c976c2a97557422b6358f983d6e6e3 100644 GIT binary patch delta 4367 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 n&BnTFC??`J7Dfw3*!B=5.5.222", + "helpPath": "", + "iconUrl": "data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iVVRGLTgiPz48IURPQ1RZUEUgc3ZnIFBVQkxJQyAiLS8vVzNDLy9EVEQgU1ZHIDEuMS8vRU4iICJodHRwOi8vd3d3LnczLm9yZy9HcmFwaGljcy9TVkcvMS4xL0RURC9zdmcxMS5kdGQiPjxzdmcgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIiB4bWxuczp4bGluaz0iaHR0cDovL3d3dy53My5vcmcvMTk5OS94bGluayIgdmVyc2lvbj0iMS4xIiBpZD0ibWRpLWFycm93LWFsbCIgd2lkdGg9IjI0IiBoZWlnaHQ9IjI0IiB2aWV3Qm94PSIwIDAgMjQgMjQiPjxwYXRoIGQ9Ik0xMywxMUgxOEwxNi41LDkuNUwxNy45Miw4LjA4TDIxLjg0LDEyTDE3LjkyLDE1LjkyTDE2LjUsMTQuNUwxOCwxM0gxM1YxOEwxNC41LDE2LjVMMTUuOTIsMTcuOTJMMTIsMjEuODRMOC4wOCwxNy45Mkw5LjUsMTYuNUwxMSwxOFYxM0g2TDcuNSwxNC41TDYuMDgsMTUuOTJMMi4xNiwxMkw2LjA4LDguMDhMNy41LDkuNUw2LDExSDExVjZMOS41LDcuNUw4LjA4LDYuMDhMMTIsMi4xNkwxNS45Miw2LjA4TDE0LjUsNy41TDEzLDZWMTFaIiAvPjwvc3ZnPg==", + "name": "ShakeObject", + "previewIconUrl": "https://resources.gdevelop-app.com/assets/Icons/arrow-all.svg", + "shortDescription": "Shake objects with configurable translation, rotation, and scale intensity.", + "version": "1.6.2", + "description": [ + "Shake an object (position, angle or scale).", + "", + "It can be used for:", + "- Hit or impact", + "- Object slowly rocking back and forth (like a ship)", + "- Simulate engine vibration, earthquake, or pulsing", + "", + "The top-down RPG example uses this extension for damage animations ([open the project online](https://editor.gdevelop.io/?project=example://top-down-rpg))." + ], + "origin": { + "identifier": "ShakeObject", + "name": "gdevelop-extension-store" + }, + "tags": [ + "shaking", + "object", + "effect", + "shake", + "scale", + "position", + "rotate", + "angle" + ], + "authorIds": [ + "gqDaZjCfevOOxBYkK6zlhtZnXCg1" + ], + "dependencies": [], + "globalVariables": [], + "sceneVariables": [], + "eventsFunctions": [], + "eventsFunctionsFolderStructure": { + "folderName": "__ROOT" + }, + "eventsBasedBehaviors": [ + { + "description": "Shake objects with translation and rotation.", + "fullName": "Shake object (position, angle)", + "helpPath": "", + "iconUrl": "", + "name": "ShakeObject_PositionAngle", + "objectType": "", + "previewIconUrl": "", + "eventsFunctions": [ + { + "description": "Shake an object, using one or more ways to shake (position, angle). Make sure to \"Stop shaking\" before starting a new shake if it uses different parameters. ", + "fullName": "Shake object (position, angle)", + "functionType": "Action", + "name": "ShakeObject_PositionAngle", + "sentence": "Shake object _PARAM0_ for _PARAM2_ seconds. Modify position amplitude _PARAM3_ on X axis and _PARAM4_ on Y axis, angle rotation amplitude _PARAM5_. Wait _PARAM6_ seconds between shakes. Keep shaking until stopped: _PARAM7_", + "events": [ + { + "colorB": 228, + "colorG": 176, + "colorR": 74, + "creationTime": 0, + "name": "Shake Object", + "source": "", + "type": "BuiltinCommonInstructions::Group", + "events": [ + { + "type": "BuiltinCommonInstructions::Comment", + "color": { + "b": 109, + "g": 230, + "r": 255, + "textB": 0, + "textG": 0, + "textR": 0 + }, + "comment": "Start/Reset duration timer" + }, + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [], + "actions": [ + { + "type": { + "value": "ResetObjectTimer" + }, + "parameters": [ + "Object", + "\"__ShakeObject_DurationTimer\"" + ] + } + ] + }, + { + "type": "BuiltinCommonInstructions::Comment", + "color": { + "b": 109, + "g": 230, + "r": 255, + "textB": 0, + "textG": 0, + "textR": 0 + }, + "comment": "Pass input parameters to global variables so that onScenePostEvents can use them" + }, + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [], + "actions": [ + { + "type": { + "value": "ModVarObjet" + }, + "parameters": [ + "Object", + "__ShakeObject_Duration", + "=", + "Duration" + ] + }, + { + "type": { + "value": "ModVarObjet" + }, + "parameters": [ + "Object", + "__ShakeObject_PowerX", + "=", + "PowerX" + ] + }, + { + "type": { + "value": "ModVarObjet" + }, + "parameters": [ + "Object", + "__ShakeObject_PowerY", + "=", + "PowerY" + ] + }, + { + "type": { + "value": "ModVarObjet" + }, + "parameters": [ + "Object", + "__ShakeObject_PowerAngle", + "=", + "PowerAngle" + ] + }, + { + "type": { + "value": "ModVarObjet" + }, + "parameters": [ + "Object", + "__ShakeObject_TimeBetweenShakes", + "=", + "TimeBetweenShakes" + ] + } + ] + }, + { + "type": "BuiltinCommonInstructions::Comment", + "color": { + "b": 109, + "g": 230, + "r": 255, + "textB": 0, + "textG": 0, + "textR": 0 + }, + "comment": "Determine if the shake should keep going until stopped" + }, + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [], + "actions": [ + { + "type": { + "value": "SetObjectVariableAsBoolean" + }, + "parameters": [ + "Object", + "__ShakeObject_ShakeForever", + "False" + ] + } + ] + }, + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [ + { + "type": { + "value": "BooleanVariable" + }, + "parameters": [ + "ShakeForever", + "True", + "" + ] + } + ], + "actions": [ + { + "type": { + "value": "SetObjectVariableAsBoolean" + }, + "parameters": [ + "Object", + "__ShakeObject_ShakeForever", + "True" + ] + } + ] + }, + { + "type": "BuiltinCommonInstructions::Comment", + "color": { + "b": 109, + "g": 230, + "r": 255, + "textB": 0, + "textG": 0, + "textR": 0 + }, + "comment": "Add default values if none were provided" + }, + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [ + { + "type": { + "value": "VarObjet" + }, + "parameters": [ + "Object", + "__ShakeObject_Duration", + "=", + "0" + ] + } + ], + "actions": [ + { + "type": { + "value": "ModVarObjet" + }, + "parameters": [ + "Object", + "__ShakeObject_Duration", + "=", + "0.5" + ] + } + ] + }, + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [ + { + "type": { + "value": "VarObjet" + }, + "parameters": [ + "Object", + "__ShakeObject_TimeBetweenShakes", + "=", + "0" + ] + } + ], + "actions": [ + { + "type": { + "value": "ModVarObjet" + }, + "parameters": [ + "Object", + "__ShakeObject_TimeBetweenShakes", + "=", + "0.08" + ] + } + ] + }, + { + "type": "BuiltinCommonInstructions::Comment", + "color": { + "b": 109, + "g": 230, + "r": 255, + "textB": 0, + "textG": 0, + "textR": 0 + }, + "comment": "If duration is less than a single shake, increase duration to make 1 full shake" + }, + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [ + { + "type": { + "value": "VarObjet" + }, + "parameters": [ + "Object", + "__ShakeObject_Duration", + "<", + "Object.Variable(__ShakeObject_TimeBetweenShakes)" + ] + } + ], + "actions": [ + { + "type": { + "value": "ModVarObjet" + }, + "parameters": [ + "Object", + "__ShakeObject_Duration", + "=", + "Object.Variable(__ShakeObject_TimeBetweenShakes)" + ] + } + ] + }, + { + "type": "BuiltinCommonInstructions::Comment", + "color": { + "b": 109, + "g": 230, + "r": 255, + "textB": 0, + "textG": 0, + "textR": 0 + }, + "comment": "Detect initial shake" + }, + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [ + { + "type": { + "value": "VarObjet" + }, + "parameters": [ + "Object", + "__ShakeObject_ShakeInProgress", + "=", + "0" + ] + } + ], + "actions": [ + { + "type": { + "value": "ModVarObjet" + }, + "parameters": [ + "Object", + "__ShakeObject_InitialShake", + "=", + "1" + ] + } + ] + }, + { + "type": "BuiltinCommonInstructions::Comment", + "color": { + "b": 109, + "g": 230, + "r": 255, + "textB": 0, + "textG": 0, + "textR": 0 + }, + "comment": "Initiate the onScenePostEvents function" + }, + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [], + "actions": [ + { + "type": { + "value": "ModVarObjet" + }, + "parameters": [ + "Object", + "__ShakeObject_ShakeInProgress", + "=", + "1" + ] + } + ] + } + ], + "parameters": [] + } + ], + "parameters": [ + { + "description": "Object", + "name": "Object", + "type": "object" + }, + { + "description": "Behavior", + "name": "Behavior", + "supplementaryInformation": "ShakeObject::ShakeObject_PositionAngle", + "type": "behavior" + }, + { + "description": "Duration of shake (in seconds) (Default: 0.5) ", + "name": "Duration", + "type": "expression" + }, + { + "description": "Amplitude of postion shake in X direction (in pixels) (For example: 5)", + "name": "PowerX", + "type": "expression" + }, + { + "description": "Amplitude of position shake in Y direction (in pixels) (For example: 5)", + "longDescription": "Use a negative number to make the single-shake move in the opposite direction.", + "name": "PowerY", + "type": "expression" + }, + { + "description": "Amplitude of angle rotation shake (in degrees) (For example: 5)", + "name": "PowerAngle", + "type": "expression" + }, + { + "description": "Amount of time between shakes (in seconds) (Default: 0.08)", + "longDescription": "For a single-shake effect, set it to the same value as \"Duration\".", + "name": "TimeBetweenShakes", + "type": "expression" + }, + { + "description": "Keep shaking until stopped", + "longDescription": "Duration value will be ignored", + "name": "ShakeForever", + "type": "yesorno" + } + ], + "objectGroups": [] + }, + { + "fullName": "", + "functionType": "Action", + "name": "doStepPostEvents", + "sentence": "", + "events": [ + { + "colorB": 228, + "colorG": 176, + "colorR": 74, + "creationTime": 0, + "name": "Shake Object", + "source": "", + "type": "BuiltinCommonInstructions::Group", + "events": [ + { + "colorB": 228, + "colorG": 176, + "colorR": 74, + "creationTime": 0, + "name": "Start shaking", + "source": "", + "type": "BuiltinCommonInstructions::Group", + "events": [ + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [ + { + "type": { + "value": "VarObjet" + }, + "parameters": [ + "Object", + "__ShakeObject_ShakeInProgress", + "=", + "1" + ] + } + ], + "actions": [], + "events": [ + { + "type": "BuiltinCommonInstructions::Comment", + "color": { + "b": 109, + "g": 230, + "r": 255, + "textB": 0, + "textG": 0, + "textR": 0 + }, + "comment": "Keep object shaking forever (if desired)" + }, + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [ + { + "type": { + "value": "ObjectVariableAsBoolean" + }, + "parameters": [ + "Object", + "__ShakeObject_ShakeForever", + "True" + ] + } + ], + "actions": [ + { + "type": { + "value": "ModVarObjet" + }, + "parameters": [ + "Object", + "__ShakeObject_Duration", + "=", + "100" + ] + }, + { + "type": { + "value": "ResetObjectTimer" + }, + "parameters": [ + "Object", + "\"__ShakeObject_DurationTimer\"" + ] + } + ] + }, + { + "colorB": 228, + "colorG": 176, + "colorR": 74, + "creationTime": 0, + "name": "Calculate movement of the shake", + "source": "", + "type": "BuiltinCommonInstructions::Group", + "events": [ + { + "type": "BuiltinCommonInstructions::Comment", + "color": { + "b": 109, + "g": 230, + "r": 255, + "textB": 0, + "textG": 0, + "textR": 0 + }, + "comment": "Run once before every shake movement" + }, + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [ + { + "type": { + "value": "BuiltinCommonInstructions::Or" + }, + "parameters": [], + "subInstructions": [ + { + "type": { + "value": "VarObjet" + }, + "parameters": [ + "Object", + "__ShakeObject_InitialShake", + "=", + "1" + ] + }, + { + "type": { + "value": "ObjectTimer" + }, + "parameters": [ + "Object", + "\"__ShakeObject_ShakeTimer\"", + "Object.Variable(__ShakeObject_TimeBetweenShakes)" + ] + } + ] + } + ], + "actions": [ + { + "type": { + "value": "ResetObjectTimer" + }, + "parameters": [ + "Object", + "\"__ShakeObject_ShakeTimer\"" + ] + }, + { + "type": { + "value": "ModVarObjet" + }, + "parameters": [ + "Object", + "__ShakeObject_ShakeCounter", + "+", + "1" + ] + } + ], + "events": [ + { + "colorB": 228, + "colorG": 176, + "colorR": 74, + "creationTime": 0, + "name": "Correct for drift and reset drift tracking variables", + "source": "", + "type": "BuiltinCommonInstructions::Group", + "events": [ + { + "type": "BuiltinCommonInstructions::Comment", + "color": { + "b": 109, + "g": 230, + "r": 255, + "textB": 0, + "textG": 0, + "textR": 0 + }, + "comment": "Move to correct any drift from previous shake" + }, + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [ + { + "type": { + "value": "BuiltinCommonInstructions::Or" + }, + "parameters": [], + "subInstructions": [ + { + "type": { + "value": "VarObjet" + }, + "parameters": [ + "Object", + "__ShakeObject_PowerX", + "!=", + "0" + ] + }, + { + "type": { + "value": "VarObjet" + }, + "parameters": [ + "Object", + "__ShakeObject_PowerY", + "!=", + "0" + ] + } + ] + } + ], + "actions": [ + { + "type": { + "value": "SetX" + }, + "parameters": [ + "Object", + "-", + "Object.Variable(__ShakeObject_DisplacementTravelledX)" + ] + }, + { + "type": { + "value": "SetY" + }, + "parameters": [ + "Object", + "-", + "Object.Variable(__ShakeObject_DisplacementTravelledY)" + ] + } + ] + }, + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [ + { + "type": { + "value": "VarObjet" + }, + "parameters": [ + "Object", + "__ShakeObject_PowerAngle", + "!=", + "0" + ] + } + ], + "actions": [ + { + "type": { + "value": "SetAngle" + }, + "parameters": [ + "Object", + "-", + "Object.Variable(__ShakeObject_AngleTravelled)" + ] + } + ] + }, + { + "type": "BuiltinCommonInstructions::Comment", + "color": { + "b": 109, + "g": 230, + "r": 255, + "textB": 0, + "textG": 0, + "textR": 0 + }, + "comment": "Reset drift detection variables" + }, + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [], + "actions": [ + { + "type": { + "value": "ModVarObjet" + }, + "parameters": [ + "Object", + "__ShakeObject_DisplacementTravelledX", + "=", + "0" + ] + }, + { + "type": { + "value": "ModVarObjet" + }, + "parameters": [ + "Object", + "__ShakeObject_DisplacementTravelledY", + "=", + "0" + ] + }, + { + "type": { + "value": "ModVarObjet" + }, + "parameters": [ + "Object", + "__ShakeObject_AngleTravelled", + "=", + "0" + ] + } + ] + } + ], + "parameters": [] + }, + { + "type": "BuiltinCommonInstructions::Comment", + "color": { + "b": 109, + "g": 230, + "r": 255, + "textB": 0, + "textG": 0, + "textR": 0 + }, + "comment": "Calculate Position Shake" + }, + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [ + { + "type": { + "value": "BuiltinCommonInstructions::Or" + }, + "parameters": [], + "subInstructions": [ + { + "type": { + "value": "VarObjet" + }, + "parameters": [ + "Object", + "__ShakeObject_PowerX", + "!=", + "0" + ] + }, + { + "type": { + "value": "VarObjet" + }, + "parameters": [ + "Object", + "__ShakeObject_PowerY", + "!=", + "0" + ] + } + ] + } + ], + "actions": [], + "events": [ + { + "type": "BuiltinCommonInstructions::Comment", + "color": { + "b": 109, + "g": 230, + "r": 255, + "textB": 0, + "textG": 0, + "textR": 0 + }, + "comment": "PositionDisplacement = (DesiredDuration - RunningTimer) / DesiredDuration * Amplitude * [-1 or 1]" + }, + { + "type": "BuiltinCommonInstructions::Comment", + "color": { + "b": 109, + "g": 230, + "r": 255, + "textB": 0, + "textG": 0, + "textR": 0 + }, + "comment": "Make initial shake NOT random so users can set a direction for a one-shake effect" + }, + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [ + { + "type": { + "value": "VarObjet" + }, + "parameters": [ + "Object", + "__ShakeObject_InitialShake", + "=", + "1" + ] + } + ], + "actions": [], + "events": [ + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [ + { + "type": { + "value": "VarObjet" + }, + "parameters": [ + "Object", + "__ShakeObject_PowerX", + "!=", + "0" + ] + } + ], + "actions": [ + { + "type": { + "value": "ModVarObjet" + }, + "parameters": [ + "Object", + "__ShakeObject_DisplacementX", + "=", + "(Object.Variable(__ShakeObject_Duration) - Object.ObjectTimerElapsedTime(\"__ShakeObject_DurationTimer\")) / Object.Variable(__ShakeObject_Duration) * Object.Variable(__ShakeObject_PowerX)" + ] + } + ] + }, + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [ + { + "type": { + "value": "VarObjet" + }, + "parameters": [ + "Object", + "__ShakeObject_PowerY", + "!=", + "0" + ] + } + ], + "actions": [ + { + "type": { + "value": "ModVarObjet" + }, + "parameters": [ + "Object", + "__ShakeObject_DisplacementY", + "=", + "(Object.Variable(__ShakeObject_Duration) - Object.ObjectTimerElapsedTime(\"__ShakeObject_DurationTimer\")) / Object.Variable(__ShakeObject_Duration) * Object.Variable(__ShakeObject_PowerY)" + ] + } + ] + } + ] + }, + { + "type": "BuiltinCommonInstructions::Comment", + "color": { + "b": 109, + "g": 230, + "r": 255, + "textB": 0, + "textG": 0, + "textR": 0 + }, + "comment": "After initial shake pick a random direction" + }, + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [ + { + "type": { + "value": "VarObjet" + }, + "parameters": [ + "Object", + "__ShakeObject_InitialShake", + "=", + "0" + ] + } + ], + "actions": [], + "events": [ + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [ + { + "type": { + "value": "VarObjet" + }, + "parameters": [ + "Object", + "__ShakeObject_PowerX", + "!=", + "0" + ] + } + ], + "actions": [ + { + "type": { + "value": "ModVarObjet" + }, + "parameters": [ + "Object", + "__ShakeObject_DisplacementX", + "=", + "(Object.Variable(__ShakeObject_Duration) - Object.ObjectTimerElapsedTime(\"__ShakeObject_DurationTimer\")) / Object.Variable(__ShakeObject_Duration) * Object.Variable(__ShakeObject_PowerX) * RandomWithStep(-1, 1, 2)" + ] + } + ] + }, + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [ + { + "type": { + "value": "VarObjet" + }, + "parameters": [ + "Object", + "__ShakeObject_PowerY", + "!=", + "0" + ] + } + ], + "actions": [ + { + "type": { + "value": "ModVarObjet" + }, + "parameters": [ + "Object", + "__ShakeObject_DisplacementY", + "=", + "(Object.Variable(__ShakeObject_Duration) - Object.ObjectTimerElapsedTime(\"__ShakeObject_DurationTimer\")) / Object.Variable(__ShakeObject_Duration) * Object.Variable(__ShakeObject_PowerY) * RandomWithStep(-1, 1, 2)" + ] + } + ] + } + ] + } + ] + }, + { + "type": "BuiltinCommonInstructions::Comment", + "color": { + "b": 109, + "g": 230, + "r": 255, + "textB": 0, + "textG": 0, + "textR": 0 + }, + "comment": "Calculate Rotation (angle) shake " + }, + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [ + { + "type": { + "value": "VarObjet" + }, + "parameters": [ + "Object", + "__ShakeObject_PowerAngle", + "!=", + "0" + ] + } + ], + "actions": [], + "events": [ + { + "type": "BuiltinCommonInstructions::Comment", + "color": { + "b": 109, + "g": 230, + "r": 255, + "textB": 0, + "textG": 0, + "textR": 0 + }, + "comment": "Every \"even\" shake, rotate clockwise" + }, + { + "type": "BuiltinCommonInstructions::Comment", + "color": { + "b": 109, + "g": 230, + "r": 255, + "textB": 0, + "textG": 0, + "textR": 0 + }, + "comment": "AngleDisplacement = (DesiredDuration - RunningTimer) / DesiredDuration * Amplitude" + }, + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [ + { + "type": { + "value": "BuiltinCommonInstructions::CompareNumbers" + }, + "parameters": [ + "mod(Object.Variable(__ShakeObject_ShakeCounter),2)", + "=", + "0" + ] + } + ], + "actions": [ + { + "type": { + "value": "ModVarObjet" + }, + "parameters": [ + "Object", + "__ShakeObject_DisplacementAngle", + "=", + "(Object.Variable(__ShakeObject_Duration) - Object.ObjectTimerElapsedTime(\"__ShakeObject_DurationTimer\")) / Object.Variable(__ShakeObject_Duration) * Object.Variable(__ShakeObject_PowerAngle)" + ] + } + ] + }, + { + "type": "BuiltinCommonInstructions::Comment", + "color": { + "b": 109, + "g": 230, + "r": 255, + "textB": 0, + "textG": 0, + "textR": 0 + }, + "comment": "Every \"odd\" shake, rotate counter-clockwise" + }, + { + "type": "BuiltinCommonInstructions::Comment", + "color": { + "b": 109, + "g": 230, + "r": 255, + "textB": 0, + "textG": 0, + "textR": 0 + }, + "comment": "AngleDisplacement = (DesiredDuration - RunningTimer) / DesiredDuration * Amplitude" + }, + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [ + { + "type": { + "value": "BuiltinCommonInstructions::CompareNumbers" + }, + "parameters": [ + "mod(Object.Variable(__ShakeObject_ShakeCounter),2)", + "=", + "1" + ] + } + ], + "actions": [ + { + "type": { + "value": "ModVarObjet" + }, + "parameters": [ + "Object", + "__ShakeObject_DisplacementAngle", + "=", + "-1 *(Object.Variable(__ShakeObject_Duration) - Object.ObjectTimerElapsedTime(\"__ShakeObject_DurationTimer\")) / Object.Variable(__ShakeObject_Duration) * Object.Variable(__ShakeObject_PowerAngle)" + ] + } + ] + } + ] + }, + { + "type": "BuiltinCommonInstructions::Comment", + "color": { + "b": 109, + "g": 230, + "r": 255, + "textB": 0, + "textG": 0, + "textR": 0 + }, + "comment": "Save that initial shake has been processed" + }, + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [], + "actions": [ + { + "type": { + "value": "ModVarObjet" + }, + "parameters": [ + "Object", + "__ShakeObject_InitialShake", + "=", + "0" + ] + } + ] + } + ] + } + ], + "parameters": [] + }, + { + "colorB": 228, + "colorG": 176, + "colorR": 74, + "creationTime": 0, + "name": "Move object", + "source": "", + "type": "BuiltinCommonInstructions::Group", + "events": [ + { + "type": "BuiltinCommonInstructions::Comment", + "color": { + "b": 109, + "g": 230, + "r": 255, + "textB": 0, + "textG": 0, + "textR": 0 + }, + "comment": "Calculate the fraction of shake that occured during this frame" + }, + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [], + "actions": [ + { + "type": { + "value": "ModVarObjet" + }, + "parameters": [ + "Object", + "__ShakeObject_PercentTimeElapsedThisFrame", + "=", + "min(1,TimeDelta()/Object.Variable(__ShakeObject_TimeBetweenShakes))" + ] + } + ] + }, + { + "type": "BuiltinCommonInstructions::Comment", + "color": { + "b": 109, + "g": 230, + "r": 255, + "textB": 0, + "textG": 0, + "textR": 0 + }, + "comment": "Increase change for the first half of the shake (move away from original values)" + }, + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [ + { + "type": { + "inverted": true, + "value": "ObjectTimer" + }, + "parameters": [ + "Object", + "\"__ShakeObject_ShakeTimer\"", + "Object.Variable(__ShakeObject_TimeBetweenShakes)/2" + ] + } + ], + "actions": [], + "events": [ + { + "type": "BuiltinCommonInstructions::Comment", + "color": { + "b": 109, + "g": 230, + "r": 255, + "textB": 0, + "textG": 0, + "textR": 0 + }, + "comment": "Change position" + }, + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [ + { + "type": { + "value": "BuiltinCommonInstructions::Or" + }, + "parameters": [], + "subInstructions": [ + { + "type": { + "value": "VarObjet" + }, + "parameters": [ + "Object", + "__ShakeObject_PowerX", + "!=", + "0" + ] + }, + { + "type": { + "value": "VarObjet" + }, + "parameters": [ + "Object", + "__ShakeObject_PowerY", + "!=", + "0" + ] + } + ] + } + ], + "actions": [ + { + "type": { + "value": "SetX" + }, + "parameters": [ + "Object", + "+", + "round(1024 * Object.Variable(__ShakeObject_DisplacementX) * Object.Variable(__ShakeObject_PercentTimeElapsedThisFrame)) / 1024" + ] + }, + { + "type": { + "value": "SetY" + }, + "parameters": [ + "Object", + "+", + "round(1024 * Object.Variable(__ShakeObject_DisplacementY) * Object.Variable(__ShakeObject_PercentTimeElapsedThisFrame)) / 1024" + ] + } + ], + "events": [ + { + "type": "BuiltinCommonInstructions::Comment", + "color": { + "b": 109, + "g": 230, + "r": 255, + "textB": 0, + "textG": 0, + "textR": 0 + }, + "comment": "Save movement to calculate drift" + }, + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [], + "actions": [ + { + "type": { + "value": "ModVarObjet" + }, + "parameters": [ + "Object", + "__ShakeObject_DisplacementTravelledX", + "+", + "round(1024 * Object.Variable(__ShakeObject_DisplacementX) * Object.Variable(__ShakeObject_PercentTimeElapsedThisFrame)) / 1024" + ] + }, + { + "type": { + "value": "ModVarObjet" + }, + "parameters": [ + "Object", + "__ShakeObject_DisplacementTravelledY", + "+", + "round(1024 * Object.Variable(__ShakeObject_DisplacementY) * Object.Variable(__ShakeObject_PercentTimeElapsedThisFrame)) / 1024" + ] + } + ] + } + ] + }, + { + "type": "BuiltinCommonInstructions::Comment", + "color": { + "b": 109, + "g": 230, + "r": 255, + "textB": 0, + "textG": 0, + "textR": 0 + }, + "comment": "Change angle" + }, + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [ + { + "type": { + "value": "BuiltinCommonInstructions::CompareNumbers" + }, + "parameters": [ + "Object.Variable(__ShakeObject_PowerAngle)", + "!=", + "0" + ] + } + ], + "actions": [ + { + "type": { + "value": "SetAngle" + }, + "parameters": [ + "Object", + "+", + "round(1024 * Object.Variable(__ShakeObject_DisplacementAngle) * Object.Variable(__ShakeObject_PercentTimeElapsedThisFrame)) / 1024" + ] + } + ], + "events": [ + { + "type": "BuiltinCommonInstructions::Comment", + "color": { + "b": 109, + "g": 230, + "r": 255, + "textB": 0, + "textG": 0, + "textR": 0 + }, + "comment": "Save movement to calculate drift" + }, + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [], + "actions": [ + { + "type": { + "value": "ModVarObjet" + }, + "parameters": [ + "Object", + "__ShakeObject_AngleTravelled", + "+", + "round(1024 * Object.Variable(__ShakeObject_DisplacementAngle) * Object.Variable(__ShakeObject_PercentTimeElapsedThisFrame)) / 1024" + ] + } + ] + } + ] + } + ] + }, + { + "type": "BuiltinCommonInstructions::Comment", + "color": { + "b": 109, + "g": 230, + "r": 255, + "textB": 0, + "textG": 0, + "textR": 0 + }, + "comment": "Decrease change the second half of the shake (return to original position)" + }, + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [ + { + "type": { + "value": "ObjectTimer" + }, + "parameters": [ + "Object", + "\"__ShakeObject_ShakeTimer\"", + "Object.Variable(__ShakeObject_TimeBetweenShakes)/2" + ] + } + ], + "actions": [], + "events": [ + { + "type": "BuiltinCommonInstructions::Comment", + "color": { + "b": 109, + "g": 230, + "r": 255, + "textB": 0, + "textG": 0, + "textR": 0 + }, + "comment": "Change position" + }, + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [ + { + "type": { + "value": "BuiltinCommonInstructions::Or" + }, + "parameters": [], + "subInstructions": [ + { + "type": { + "value": "VarObjet" + }, + "parameters": [ + "Object", + "__ShakeObject_PowerX", + "!=", + "0" + ] + }, + { + "type": { + "value": "VarObjet" + }, + "parameters": [ + "Object", + "__ShakeObject_PowerY", + "!=", + "0" + ] + } + ] + } + ], + "actions": [ + { + "type": { + "value": "SetX" + }, + "parameters": [ + "Object", + "-", + "round(1024 * Object.Variable(__ShakeObject_DisplacementX) * Object.Variable(__ShakeObject_PercentTimeElapsedThisFrame)) / 1024" + ] + }, + { + "type": { + "value": "SetY" + }, + "parameters": [ + "Object", + "-", + "round(1024 * Object.Variable(__ShakeObject_DisplacementY) * Object.Variable(__ShakeObject_PercentTimeElapsedThisFrame)) / 1024" + ] + } + ], + "events": [ + { + "type": "BuiltinCommonInstructions::Comment", + "color": { + "b": 109, + "g": 230, + "r": 255, + "textB": 0, + "textG": 0, + "textR": 0 + }, + "comment": "Save movement to calculate drift" + }, + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [], + "actions": [ + { + "type": { + "value": "ModVarObjet" + }, + "parameters": [ + "Object", + "__ShakeObject_DisplacementTravelledX", + "-", + "round(1024 * Object.Variable(__ShakeObject_DisplacementX) * Object.Variable(__ShakeObject_PercentTimeElapsedThisFrame)) / 1024" + ] + }, + { + "type": { + "value": "ModVarObjet" + }, + "parameters": [ + "Object", + "__ShakeObject_DisplacementTravelledY", + "-", + "round(1024 * Object.Variable(__ShakeObject_DisplacementY) * Object.Variable(__ShakeObject_PercentTimeElapsedThisFrame)) / 1024" + ] + } + ] + } + ] + }, + { + "type": "BuiltinCommonInstructions::Comment", + "color": { + "b": 109, + "g": 230, + "r": 255, + "textB": 0, + "textG": 0, + "textR": 0 + }, + "comment": "Change angle" + }, + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [ + { + "type": { + "value": "BuiltinCommonInstructions::CompareNumbers" + }, + "parameters": [ + "Object.Variable(__ShakeObject_PowerAngle)", + "!=", + "0" + ] + } + ], + "actions": [ + { + "type": { + "value": "SetAngle" + }, + "parameters": [ + "Object", + "-", + "round(1024 * Object.Variable(__ShakeObject_DisplacementAngle) * Object.Variable(__ShakeObject_PercentTimeElapsedThisFrame)) / 1024" + ] + } + ], + "events": [ + { + "type": "BuiltinCommonInstructions::Comment", + "color": { + "b": 109, + "g": 230, + "r": 255, + "textB": 0, + "textG": 0, + "textR": 0 + }, + "comment": "Save movement to calculate drift" + }, + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [], + "actions": [ + { + "type": { + "value": "ModVarObjet" + }, + "parameters": [ + "Object", + "__ShakeObject_AngleTravelled", + "-", + "round(1024 * Object.Variable(__ShakeObject_DisplacementAngle) * Object.Variable(__ShakeObject_PercentTimeElapsedThisFrame)) / 1024" + ] + } + ] + } + ] + } + ] + } + ], + "parameters": [] + } + ] + } + ], + "parameters": [] + }, + { + "colorB": 228, + "colorG": 176, + "colorR": 74, + "creationTime": 0, + "name": "Stop shaking", + "source": "", + "type": "BuiltinCommonInstructions::Group", + "events": [ + { + "type": "BuiltinCommonInstructions::Comment", + "color": { + "b": 109, + "g": 230, + "r": 255, + "textB": 0, + "textG": 0, + "textR": 0 + }, + "comment": "Stop shaking when the duration has been reached (or if the stop shaking function was called)" + }, + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [ + { + "type": { + "value": "BuiltinCommonInstructions::Or" + }, + "parameters": [], + "subInstructions": [ + { + "type": { + "value": "ObjectTimer" + }, + "parameters": [ + "Object", + "\"__ShakeObject_DurationTimer\"", + "Object.Variable(__ShakeObject_Duration)" + ] + }, + { + "type": { + "value": "VarObjet" + }, + "parameters": [ + "Object", + "__ShakeObject_ShakeInProgress", + "=", + "-1" + ] + } + ] + }, + { + "type": { + "value": "BuiltinCommonInstructions::Once" + }, + "parameters": [] + } + ], + "actions": [ + { + "type": { + "value": "ModVarObjet" + }, + "parameters": [ + "Object", + "__ShakeObject_ShakeInProgress", + "=", + "0" + ] + }, + { + "type": { + "value": "ModVarObjet" + }, + "parameters": [ + "Object", + "__ShakeObject_ShakeCounter", + "=", + "0" + ] + } + ], + "events": [ + { + "colorB": 228, + "colorG": 176, + "colorR": 74, + "creationTime": 0, + "name": "Correct for drift and reset drift tracking variables", + "source": "", + "type": "BuiltinCommonInstructions::Group", + "events": [ + { + "type": "BuiltinCommonInstructions::Comment", + "color": { + "b": 109, + "g": 230, + "r": 255, + "textB": 0, + "textG": 0, + "textR": 0 + }, + "comment": "Move to correct any drift from previous shake" + }, + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [ + { + "type": { + "value": "BuiltinCommonInstructions::Or" + }, + "parameters": [], + "subInstructions": [ + { + "type": { + "value": "VarObjet" + }, + "parameters": [ + "Object", + "__ShakeObject_PowerX", + "!=", + "0" + ] + }, + { + "type": { + "value": "VarObjet" + }, + "parameters": [ + "Object", + "__ShakeObject_PowerY", + "!=", + "0" + ] + } + ] + } + ], + "actions": [ + { + "type": { + "value": "SetX" + }, + "parameters": [ + "Object", + "-", + "Object.Variable(__ShakeObject_DisplacementTravelledX)" + ] + }, + { + "type": { + "value": "SetY" + }, + "parameters": [ + "Object", + "-", + "Object.Variable(__ShakeObject_DisplacementTravelledY)" + ] + } + ] + }, + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [ + { + "type": { + "value": "VarObjet" + }, + "parameters": [ + "Object", + "__ShakeObject_PowerAngle", + "!=", + "0" + ] + } + ], + "actions": [ + { + "type": { + "value": "SetAngle" + }, + "parameters": [ + "Object", + "-", + "Object.Variable(__ShakeObject_AngleTravelled)" + ] + } + ] + }, + { + "type": "BuiltinCommonInstructions::Comment", + "color": { + "b": 109, + "g": 230, + "r": 255, + "textB": 0, + "textG": 0, + "textR": 0 + }, + "comment": "Reset drift detection variables" + }, + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [], + "actions": [ + { + "type": { + "value": "ModVarObjet" + }, + "parameters": [ + "Object", + "__ShakeObject_DisplacementTravelledX", + "=", + "0" + ] + }, + { + "type": { + "value": "ModVarObjet" + }, + "parameters": [ + "Object", + "__ShakeObject_DisplacementTravelledY", + "=", + "0" + ] + }, + { + "type": { + "value": "ModVarObjet" + }, + "parameters": [ + "Object", + "__ShakeObject_AngleTravelled", + "=", + "0" + ] + } + ] + } + ], + "parameters": [] + } + ] + } + ], + "parameters": [] + } + ], + "parameters": [] + } + ], + "parameters": [ + { + "description": "Object", + "name": "Object", + "type": "object" + }, + { + "description": "Behavior", + "name": "Behavior", + "supplementaryInformation": "ShakeObject::ShakeObject_PositionAngle", + "type": "behavior" + } + ], + "objectGroups": [] + }, + { + "description": "Stop any shaking of object that was initiated by the Shake Object extension.", + "fullName": "Stop shaking the object", + "functionType": "Action", + "name": "StopShaking", + "sentence": "Stop shaking _PARAM0_", + "events": [ + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [ + { + "type": { + "value": "ShakeObject::ShakeObject_PositionAngle::IsShaking" + }, + "parameters": [ + "Object", + "Behavior", + "" + ] + } + ], + "actions": [ + { + "type": { + "value": "ModVarObjet" + }, + "parameters": [ + "Object", + "__ShakeObject_ShakeInProgress", + "=", + "-1" + ] + } + ] + } + ], + "parameters": [ + { + "description": "Object", + "name": "Object", + "type": "object" + }, + { + "description": "Behavior", + "name": "Behavior", + "supplementaryInformation": "ShakeObject::ShakeObject_PositionAngle", + "type": "behavior" + } + ], + "objectGroups": [] + }, + { + "description": "Check if an object is shaking.", + "fullName": "Check if an object is shaking", + "functionType": "Condition", + "name": "IsShaking", + "sentence": "_PARAM0_ is shaking", + "events": [ + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [ + { + "type": { + "value": "VarObjet" + }, + "parameters": [ + "Object", + "__ShakeObject_ShakeInProgress", + "=", + "1" + ] + } + ], + "actions": [ + { + "type": { + "value": "SetReturnBoolean" + }, + "parameters": [ + "True" + ] + } + ] + } + ], + "parameters": [ + { + "description": "Object", + "name": "Object", + "type": "object" + }, + { + "description": "Behavior", + "name": "Behavior", + "supplementaryInformation": "ShakeObject::ShakeObject_PositionAngle", + "type": "behavior" + } + ], + "objectGroups": [] + } + ], + "eventsFunctionsFolderStructure": { + "folderName": "__ROOT", + "children": [ + { + "functionName": "ShakeObject_PositionAngle" + }, + { + "functionName": "doStepPostEvents" + }, + { + "functionName": "StopShaking" + }, + { + "functionName": "IsShaking" + } + ] + }, + "propertyDescriptors": [], + "propertiesFolderStructure": { + "folderName": "__ROOT" + } + }, + { + "description": "Shake objects with translation, rotation and scale.", + "fullName": "Shake object (position, angle, scale)", + "helpPath": "", + "iconUrl": "", + "name": "ShakeObject_PositionAngleScale", + "objectType": "", + "previewIconUrl": "", + "eventsFunctions": [ + { + "description": "Shake an object, using one or more ways to shake (position, angle, scale). Make sure to \"Stop shaking\" before starting a new shake if it uses different parameters.", + "fullName": "Shake object (position, angle, scale)", + "functionType": "Action", + "name": "ShakeObject_PositionAngleScale", + "sentence": "Shake object _PARAM0_ for _PARAM2_ seconds. Modify position amplitude _PARAM3_ on X axis and _PARAM4_ on Y axis, angle rotation amplitude _PARAM5_, and scale amplitude _PARAM6_. Wait _PARAM7_ seconds between shakes. Keep shaking until stopped: _PARAM8_", + "events": [ + { + "colorB": 228, + "colorG": 176, + "colorR": 74, + "creationTime": 0, + "name": "Shake Object", + "source": "", + "type": "BuiltinCommonInstructions::Group", + "events": [ + { + "type": "BuiltinCommonInstructions::Comment", + "color": { + "b": 109, + "g": 230, + "r": 255, + "textB": 0, + "textG": 0, + "textR": 0 + }, + "comment": "Start/Reset duration timer" + }, + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [], + "actions": [ + { + "type": { + "value": "ResetObjectTimer" + }, + "parameters": [ + "Object", + "\"__ShakeObject_DurationTimer\"" + ] + } + ] + }, + { + "type": "BuiltinCommonInstructions::Comment", + "color": { + "b": 109, + "g": 230, + "r": 255, + "textB": 0, + "textG": 0, + "textR": 0 + }, + "comment": "Pass input parameters to global variables so that onScenePostEvents can use them" + }, + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [], + "actions": [ + { + "type": { + "value": "ModVarObjet" + }, + "parameters": [ + "Object", + "__ShakeObject_Duration", + "=", + "Duration" + ] + }, + { + "type": { + "value": "ModVarObjet" + }, + "parameters": [ + "Object", + "__ShakeObject_PowerX", + "=", + "PowerX" + ] + }, + { + "type": { + "value": "ModVarObjet" + }, + "parameters": [ + "Object", + "__ShakeObject_PowerY", + "=", + "PowerY" + ] + }, + { + "type": { + "value": "ModVarObjet" + }, + "parameters": [ + "Object", + "__ShakeObject_PowerAngle", + "=", + "PowerAngle" + ] + }, + { + "type": { + "value": "ModVarObjet" + }, + "parameters": [ + "Object", + "__ShakeObject_PowerScale", + "=", + "PowerScale" + ] + }, + { + "type": { + "value": "ModVarObjet" + }, + "parameters": [ + "Object", + "__ShakeObject_TimeBetweenShakes", + "=", + "TimeBetweenShakes" + ] + } + ] + }, + { + "type": "BuiltinCommonInstructions::Comment", + "color": { + "b": 109, + "g": 230, + "r": 255, + "textB": 0, + "textG": 0, + "textR": 0 + }, + "comment": "Determine if the shake should keep going until stopped" + }, + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [], + "actions": [ + { + "type": { + "value": "SetObjectVariableAsBoolean" + }, + "parameters": [ + "Object", + "__ShakeObject_ShakeForever", + "False" + ] + } + ] + }, + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [ + { + "type": { + "value": "BooleanVariable" + }, + "parameters": [ + "ShakeForever", + "True", + "" + ] + } + ], + "actions": [ + { + "type": { + "value": "SetObjectVariableAsBoolean" + }, + "parameters": [ + "Object", + "__ShakeObject_ShakeForever", + "True" + ] + } + ] + }, + { + "type": "BuiltinCommonInstructions::Comment", + "color": { + "b": 109, + "g": 230, + "r": 255, + "textB": 0, + "textG": 0, + "textR": 0 + }, + "comment": "Add default values if none were provided" + }, + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [ + { + "type": { + "value": "VarObjet" + }, + "parameters": [ + "Object", + "__ShakeObject_Duration", + "=", + "0" + ] + } + ], + "actions": [ + { + "type": { + "value": "ModVarObjet" + }, + "parameters": [ + "Object", + "__ShakeObject_Duration", + "=", + "0.5" + ] + } + ] + }, + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [ + { + "type": { + "value": "VarObjet" + }, + "parameters": [ + "Object", + "__ShakeObject_TimeBetweenShakes", + "=", + "0" + ] + } + ], + "actions": [ + { + "type": { + "value": "ModVarObjet" + }, + "parameters": [ + "Object", + "__ShakeObject_TimeBetweenShakes", + "=", + "0.08" + ] + } + ] + }, + { + "type": "BuiltinCommonInstructions::Comment", + "color": { + "b": 109, + "g": 230, + "r": 255, + "textB": 0, + "textG": 0, + "textR": 0 + }, + "comment": "If duration is less than a single shake, increase duration to make 1 full shake" + }, + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [ + { + "type": { + "value": "VarObjet" + }, + "parameters": [ + "Object", + "__ShakeObject_Duration", + "<", + "Object.Variable(__ShakeObject_TimeBetweenShakes)" + ] + } + ], + "actions": [ + { + "type": { + "value": "ModVarObjet" + }, + "parameters": [ + "Object", + "__ShakeObject_Duration", + "=", + "Object.Variable(__ShakeObject_TimeBetweenShakes)" + ] + } + ] + }, + { + "type": "BuiltinCommonInstructions::Comment", + "color": { + "b": 109, + "g": 230, + "r": 255, + "textB": 0, + "textG": 0, + "textR": 0 + }, + "comment": "Detect initial shake" + }, + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [ + { + "type": { + "value": "VarObjet" + }, + "parameters": [ + "Object", + "__ShakeObject_ShakeInProgress", + "=", + "0" + ] + } + ], + "actions": [ + { + "type": { + "value": "ModVarObjet" + }, + "parameters": [ + "Object", + "__ShakeObject_InitialShake", + "=", + "1" + ] + } + ] + }, + { + "type": "BuiltinCommonInstructions::Comment", + "color": { + "b": 109, + "g": 230, + "r": 255, + "textB": 0, + "textG": 0, + "textR": 0 + }, + "comment": "Initiate the onScenePostEvents function" + }, + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [], + "actions": [ + { + "type": { + "value": "ModVarObjet" + }, + "parameters": [ + "Object", + "__ShakeObject_ShakeInProgress", + "=", + "1" + ] + } + ] + } + ], + "parameters": [] + } + ], + "parameters": [ + { + "description": "Object", + "name": "Object", + "type": "object" + }, + { + "description": "Behavior", + "name": "Behavior", + "supplementaryInformation": "ShakeObject::ShakeObject_PositionAngleScale", + "type": "behavior" + }, + { + "description": "Duration of shake (in seconds) (Default: 0.5)", + "name": "Duration", + "type": "expression" + }, + { + "description": "Amplitude of postion shake in X direction (in pixels) (For example: 5)", + "name": "PowerX", + "type": "expression" + }, + { + "description": "Amplitude of position shake in Y direction (in pixels) (For example: 5)", + "longDescription": "Use a negative number to make the single-shake move in the opposite direction.", + "name": "PowerY", + "type": "expression" + }, + { + "description": "Amplitude of angle rotation shake (in degrees) (For example: 5)", + "name": "PowerAngle", + "type": "expression" + }, + { + "description": "Amplitude of scale shake (in percent change) (For example: 5)", + "name": "PowerScale", + "type": "expression" + }, + { + "description": "Amount of time between shakes (in seconds) (Default: 0.08)", + "longDescription": "For a single-shake effect, set it to the same value as \"Duration\".", + "name": "TimeBetweenShakes", + "type": "expression" + }, + { + "description": "Keep shaking until stopped", + "longDescription": "Duration value will be ignored", + "name": "ShakeForever", + "type": "yesorno" + } + ], + "objectGroups": [] + }, + { + "fullName": "", + "functionType": "Action", + "name": "doStepPostEvents", + "sentence": "", + "events": [ + { + "colorB": 228, + "colorG": 176, + "colorR": 74, + "creationTime": 0, + "name": "Shake Object", + "source": "", + "type": "BuiltinCommonInstructions::Group", + "events": [ + { + "colorB": 228, + "colorG": 176, + "colorR": 74, + "creationTime": 0, + "name": "Start shaking", + "source": "", + "type": "BuiltinCommonInstructions::Group", + "events": [ + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [ + { + "type": { + "value": "VarObjet" + }, + "parameters": [ + "Object", + "__ShakeObject_ShakeInProgress", + "=", + "1" + ] + } + ], + "actions": [], + "events": [ + { + "type": "BuiltinCommonInstructions::Comment", + "color": { + "b": 109, + "g": 230, + "r": 255, + "textB": 0, + "textG": 0, + "textR": 0 + }, + "comment": "Keep object shaking forever (if desired)" + }, + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [ + { + "type": { + "value": "ObjectVariableAsBoolean" + }, + "parameters": [ + "Object", + "__ShakeObject_ShakeForever", + "True" + ] + } + ], + "actions": [ + { + "type": { + "value": "ModVarObjet" + }, + "parameters": [ + "Object", + "__ShakeObject_Duration", + "=", + "100" + ] + }, + { + "type": { + "value": "ResetObjectTimer" + }, + "parameters": [ + "Object", + "\"__ShakeObject_DurationTimer\"" + ] + } + ] + }, + { + "colorB": 228, + "colorG": 176, + "colorR": 74, + "creationTime": 0, + "name": "Calculate movement of the shake", + "source": "", + "type": "BuiltinCommonInstructions::Group", + "events": [ + { + "type": "BuiltinCommonInstructions::Comment", + "color": { + "b": 109, + "g": 230, + "r": 255, + "textB": 0, + "textG": 0, + "textR": 0 + }, + "comment": "Run once before every shake movement" + }, + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [ + { + "type": { + "value": "BuiltinCommonInstructions::Or" + }, + "parameters": [], + "subInstructions": [ + { + "type": { + "value": "VarObjet" + }, + "parameters": [ + "Object", + "__ShakeObject_InitialShake", + "=", + "1" + ] + }, + { + "type": { + "value": "ObjectTimer" + }, + "parameters": [ + "Object", + "\"__ShakeObject_ShakeTimer\"", + "Object.Variable(__ShakeObject_TimeBetweenShakes)" + ] + } + ] + } + ], + "actions": [ + { + "type": { + "value": "ResetObjectTimer" + }, + "parameters": [ + "Object", + "\"__ShakeObject_ShakeTimer\"" + ] + }, + { + "type": { + "value": "ModVarObjet" + }, + "parameters": [ + "Object", + "__ShakeObject_ShakeCounter", + "+", + "1" + ] + } + ], + "events": [ + { + "colorB": 228, + "colorG": 176, + "colorR": 74, + "creationTime": 0, + "folded": true, + "name": "Correct for drift and reset drift tracking variables", + "source": "", + "type": "BuiltinCommonInstructions::Group", + "events": [ + { + "type": "BuiltinCommonInstructions::Comment", + "color": { + "b": 109, + "g": 230, + "r": 255, + "textB": 0, + "textG": 0, + "textR": 0 + }, + "comment": "Move to correct any drift from previous shake" + }, + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [ + { + "type": { + "value": "BuiltinCommonInstructions::Or" + }, + "parameters": [], + "subInstructions": [ + { + "type": { + "value": "VarObjet" + }, + "parameters": [ + "Object", + "__ShakeObject_PowerX", + "!=", + "0" + ] + }, + { + "type": { + "value": "VarObjet" + }, + "parameters": [ + "Object", + "__ShakeObject_PowerY", + "!=", + "0" + ] + } + ] + } + ], + "actions": [ + { + "type": { + "value": "SetX" + }, + "parameters": [ + "Object", + "-", + "Object.Variable(__ShakeObject_DisplacementTravelledX)" + ] + }, + { + "type": { + "value": "SetY" + }, + "parameters": [ + "Object", + "-", + "Object.Variable(__ShakeObject_DisplacementTravelledY)" + ] + } + ] + }, + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [ + { + "type": { + "value": "VarObjet" + }, + "parameters": [ + "Object", + "__ShakeObject_PowerAngle", + "!=", + "0" + ] + } + ], + "actions": [ + { + "type": { + "value": "SetAngle" + }, + "parameters": [ + "Object", + "-", + "Object.Variable(__ShakeObject_AngleTravelled)" + ] + } + ] + }, + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [ + { + "type": { + "value": "VarObjet" + }, + "parameters": [ + "Object", + "__ShakeObject_PowerScale", + "!=", + "0" + ] + } + ], + "actions": [ + { + "type": { + "value": "ScalableCapability::ScalableBehavior::SetValue" + }, + "parameters": [ + "Object", + "Scale", + "-", + "Object.Variable(__ShakeObject_ScaleTravelled)" + ] + } + ] + }, + { + "type": "BuiltinCommonInstructions::Comment", + "color": { + "b": 109, + "g": 230, + "r": 255, + "textB": 0, + "textG": 0, + "textR": 0 + }, + "comment": "Reset drift detection variables" + }, + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [], + "actions": [ + { + "type": { + "value": "ModVarObjet" + }, + "parameters": [ + "Object", + "__ShakeObject_DisplacementTravelledX", + "=", + "0" + ] + }, + { + "type": { + "value": "ModVarObjet" + }, + "parameters": [ + "Object", + "__ShakeObject_DisplacementTravelledY", + "=", + "0" + ] + }, + { + "type": { + "value": "ModVarObjet" + }, + "parameters": [ + "Object", + "__ShakeObject_AngleTravelled", + "=", + "0" + ] + }, + { + "type": { + "value": "ModVarObjet" + }, + "parameters": [ + "Object", + "__ShakeObject_ScaleTravelled", + "=", + "0" + ] + } + ] + } + ], + "parameters": [] + }, + { + "type": "BuiltinCommonInstructions::Comment", + "color": { + "b": 109, + "g": 230, + "r": 255, + "textB": 0, + "textG": 0, + "textR": 0 + }, + "comment": "Calculate Position Shake" + }, + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [ + { + "type": { + "value": "BuiltinCommonInstructions::Or" + }, + "parameters": [], + "subInstructions": [ + { + "type": { + "value": "VarObjet" + }, + "parameters": [ + "Object", + "__ShakeObject_PowerX", + "!=", + "0" + ] + }, + { + "type": { + "value": "VarObjet" + }, + "parameters": [ + "Object", + "__ShakeObject_PowerY", + "!=", + "0" + ] + } + ] + } + ], + "actions": [], + "events": [ + { + "type": "BuiltinCommonInstructions::Comment", + "color": { + "b": 109, + "g": 230, + "r": 255, + "textB": 0, + "textG": 0, + "textR": 0 + }, + "comment": "PositionDisplacement = (DesiredDuration - RunningTimer) / DesiredDuration * Amplitude * [-1 or 1]" + }, + { + "type": "BuiltinCommonInstructions::Comment", + "color": { + "b": 109, + "g": 230, + "r": 255, + "textB": 0, + "textG": 0, + "textR": 0 + }, + "comment": "Make initial shake NOT random so users can set a direction for a one-shake effect" + }, + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [ + { + "type": { + "value": "VarObjet" + }, + "parameters": [ + "Object", + "__ShakeObject_InitialShake", + "=", + "1" + ] + } + ], + "actions": [], + "events": [ + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [ + { + "type": { + "value": "VarObjet" + }, + "parameters": [ + "Object", + "__ShakeObject_PowerX", + "!=", + "0" + ] + } + ], + "actions": [ + { + "type": { + "value": "ModVarObjet" + }, + "parameters": [ + "Object", + "__ShakeObject_DisplacementX", + "=", + "(Object.Variable(__ShakeObject_Duration) - Object.ObjectTimerElapsedTime(\"__ShakeObject_DurationTimer\")) / Object.Variable(__ShakeObject_Duration) * Object.Variable(__ShakeObject_PowerX)" + ] + } + ] + }, + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [ + { + "type": { + "value": "VarObjet" + }, + "parameters": [ + "Object", + "__ShakeObject_PowerY", + "!=", + "0" + ] + } + ], + "actions": [ + { + "type": { + "value": "ModVarObjet" + }, + "parameters": [ + "Object", + "__ShakeObject_DisplacementY", + "=", + "(Object.Variable(__ShakeObject_Duration) - Object.ObjectTimerElapsedTime(\"__ShakeObject_DurationTimer\")) / Object.Variable(__ShakeObject_Duration) * Object.Variable(__ShakeObject_PowerY)" + ] + } + ] + } + ] + }, + { + "type": "BuiltinCommonInstructions::Comment", + "color": { + "b": 109, + "g": 230, + "r": 255, + "textB": 0, + "textG": 0, + "textR": 0 + }, + "comment": "After initial shake pick a random direction" + }, + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [ + { + "type": { + "value": "VarObjet" + }, + "parameters": [ + "Object", + "__ShakeObject_InitialShake", + "=", + "0" + ] + } + ], + "actions": [], + "events": [ + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [ + { + "type": { + "value": "VarObjet" + }, + "parameters": [ + "Object", + "__ShakeObject_PowerX", + "!=", + "0" + ] + } + ], + "actions": [ + { + "type": { + "value": "ModVarObjet" + }, + "parameters": [ + "Object", + "__ShakeObject_DisplacementX", + "=", + "(Object.Variable(__ShakeObject_Duration) - Object.ObjectTimerElapsedTime(\"__ShakeObject_DurationTimer\")) / Object.Variable(__ShakeObject_Duration) * Object.Variable(__ShakeObject_PowerX) * RandomWithStep(-1, 1, 2)" + ] + } + ] + }, + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [ + { + "type": { + "value": "VarObjet" + }, + "parameters": [ + "Object", + "__ShakeObject_PowerY", + "!=", + "0" + ] + } + ], + "actions": [ + { + "type": { + "value": "ModVarObjet" + }, + "parameters": [ + "Object", + "__ShakeObject_DisplacementY", + "=", + "(Object.Variable(__ShakeObject_Duration) - Object.ObjectTimerElapsedTime(\"__ShakeObject_DurationTimer\")) / Object.Variable(__ShakeObject_Duration) * Object.Variable(__ShakeObject_PowerY) * RandomWithStep(-1, 1, 2)" + ] + } + ] + } + ] + } + ] + }, + { + "type": "BuiltinCommonInstructions::Comment", + "color": { + "b": 109, + "g": 230, + "r": 255, + "textB": 0, + "textG": 0, + "textR": 0 + }, + "comment": "Calculate Rotation (angle) shake " + }, + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [ + { + "type": { + "value": "VarObjet" + }, + "parameters": [ + "Object", + "__ShakeObject_PowerAngle", + "!=", + "0" + ] + } + ], + "actions": [], + "events": [ + { + "type": "BuiltinCommonInstructions::Comment", + "color": { + "b": 109, + "g": 230, + "r": 255, + "textB": 0, + "textG": 0, + "textR": 0 + }, + "comment": "Every \"even\" shake, rotate clockwise" + }, + { + "type": "BuiltinCommonInstructions::Comment", + "color": { + "b": 109, + "g": 230, + "r": 255, + "textB": 0, + "textG": 0, + "textR": 0 + }, + "comment": "AngleDisplacement = (DesiredDuration - RunningTimer) / DesiredDuration * Amplitude" + }, + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [ + { + "type": { + "value": "BuiltinCommonInstructions::CompareNumbers" + }, + "parameters": [ + "mod(Object.Variable(__ShakeObject_ShakeCounter),2)", + "=", + "0" + ] + } + ], + "actions": [ + { + "type": { + "value": "ModVarObjet" + }, + "parameters": [ + "Object", + "__ShakeObject_DisplacementAngle", + "=", + "-1 * (Object.Variable(__ShakeObject_Duration) - Object.ObjectTimerElapsedTime(\"__ShakeObject_DurationTimer\")) / Object.Variable(__ShakeObject_Duration) * Object.Variable(__ShakeObject_PowerAngle)" + ] + } + ] + }, + { + "type": "BuiltinCommonInstructions::Comment", + "color": { + "b": 109, + "g": 230, + "r": 255, + "textB": 0, + "textG": 0, + "textR": 0 + }, + "comment": "Every \"odd\" shake, rotate counter-clockwise" + }, + { + "type": "BuiltinCommonInstructions::Comment", + "color": { + "b": 109, + "g": 230, + "r": 255, + "textB": 0, + "textG": 0, + "textR": 0 + }, + "comment": "AngleDisplacement = (DesiredDuration - RunningTimer) / DesiredDuration * Amplitude" + }, + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [ + { + "type": { + "value": "BuiltinCommonInstructions::CompareNumbers" + }, + "parameters": [ + "mod(Object.Variable(__ShakeObject_ShakeCounter),2)", + "=", + "1" + ] + } + ], + "actions": [ + { + "type": { + "value": "ModVarObjet" + }, + "parameters": [ + "Object", + "__ShakeObject_DisplacementAngle", + "=", + "(Object.Variable(__ShakeObject_Duration) - Object.ObjectTimerElapsedTime(\"__ShakeObject_DurationTimer\")) / Object.Variable(__ShakeObject_Duration) * Object.Variable(__ShakeObject_PowerAngle)" + ] + } + ] + } + ] + }, + { + "type": "BuiltinCommonInstructions::Comment", + "color": { + "b": 109, + "g": 230, + "r": 255, + "textB": 0, + "textG": 0, + "textR": 0 + }, + "comment": "Calculate Scale shake" + }, + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [ + { + "type": { + "value": "VarObjet" + }, + "parameters": [ + "Object", + "__ShakeObject_PowerScale", + "!=", + "0" + ] + } + ], + "actions": [], + "events": [ + { + "type": "BuiltinCommonInstructions::Comment", + "color": { + "b": 109, + "g": 230, + "r": 255, + "textB": 0, + "textG": 0, + "textR": 0 + }, + "comment": "Calculate object scale displacement, with linear decay over time" + }, + { + "type": "BuiltinCommonInstructions::Comment", + "color": { + "b": 109, + "g": 230, + "r": 255, + "textB": 0, + "textG": 0, + "textR": 0 + }, + "comment": "ScaleDisplacement = (DesiredDuration - RunningTimer) / DesiredDuration * Amplitude * 1/100" + }, + { + "type": "BuiltinCommonInstructions::Comment", + "color": { + "b": 109, + "g": 230, + "r": 255, + "textB": 0, + "textG": 0, + "textR": 0 + }, + "comment": "Every even shake, increase scale" + }, + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [ + { + "type": { + "value": "BuiltinCommonInstructions::CompareNumbers" + }, + "parameters": [ + "mod(Object.Variable(__ShakeObject_ShakeCounter),2)", + "=", + "0" + ] + } + ], + "actions": [ + { + "type": { + "value": "ModVarObjet" + }, + "parameters": [ + "Object", + "__ShakeObject_DisplacementScale", + "=", + "-1 * (Object.Variable(__ShakeObject_Duration) - Object.ObjectTimerElapsedTime(\"__ShakeObject_DurationTimer\")) / Object.Variable(__ShakeObject_Duration) * Object.Variable(__ShakeObject_PowerScale) * (1/100)" + ] + } + ] + }, + { + "type": "BuiltinCommonInstructions::Comment", + "color": { + "b": 109, + "g": 230, + "r": 255, + "textB": 0, + "textG": 0, + "textR": 0 + }, + "comment": "Every \"odd\" shake, decrease scale" + }, + { + "type": "BuiltinCommonInstructions::Comment", + "color": { + "b": 109, + "g": 230, + "r": 255, + "textB": 0, + "textG": 0, + "textR": 0 + }, + "comment": "ScaleDisplacement = -1 * (DesiredDuration - RunningTimer) / DesiredDuration * Amplitude * 1/100" + }, + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [ + { + "type": { + "value": "BuiltinCommonInstructions::CompareNumbers" + }, + "parameters": [ + "mod(Object.Variable(__ShakeObject_ShakeCounter),2)", + "=", + "1" + ] + } + ], + "actions": [ + { + "type": { + "value": "ModVarObjet" + }, + "parameters": [ + "Object", + "__ShakeObject_DisplacementScale", + "=", + "(Object.Variable(__ShakeObject_Duration) - Object.ObjectTimerElapsedTime(\"__ShakeObject_DurationTimer\")) / Object.Variable(__ShakeObject_Duration) * Object.Variable(__ShakeObject_PowerScale) * (1/100)" + ] + } + ] + } + ] + }, + { + "type": "BuiltinCommonInstructions::Comment", + "color": { + "b": 109, + "g": 230, + "r": 255, + "textB": 0, + "textG": 0, + "textR": 0 + }, + "comment": "Save that initial shake has been processed" + }, + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [], + "actions": [ + { + "type": { + "value": "ModVarObjet" + }, + "parameters": [ + "Object", + "__ShakeObject_InitialShake", + "=", + "0" + ] + } + ] + } + ] + } + ], + "parameters": [] + }, + { + "colorB": 228, + "colorG": 176, + "colorR": 74, + "creationTime": 0, + "name": "Move object", + "source": "", + "type": "BuiltinCommonInstructions::Group", + "events": [ + { + "type": "BuiltinCommonInstructions::Comment", + "color": { + "b": 109, + "g": 230, + "r": 255, + "textB": 0, + "textG": 0, + "textR": 0 + }, + "comment": "Calculate the fraction of shake that occured during this frame" + }, + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [], + "actions": [ + { + "type": { + "value": "ModVarObjet" + }, + "parameters": [ + "Object", + "__ShakeObject_PercentTimeElapsedThisFrame", + "=", + "min(1,TimeDelta()/Object.Variable(__ShakeObject_TimeBetweenShakes))" + ] + } + ] + }, + { + "type": "BuiltinCommonInstructions::Comment", + "color": { + "b": 109, + "g": 230, + "r": 255, + "textB": 0, + "textG": 0, + "textR": 0 + }, + "comment": "Increase change for the first half of the shake (move away from original values)" + }, + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [ + { + "type": { + "inverted": true, + "value": "ObjectTimer" + }, + "parameters": [ + "Object", + "\"__ShakeObject_ShakeTimer\"", + "Object.Variable(__ShakeObject_TimeBetweenShakes)/2" + ] + } + ], + "actions": [], + "events": [ + { + "type": "BuiltinCommonInstructions::Comment", + "color": { + "b": 109, + "g": 230, + "r": 255, + "textB": 0, + "textG": 0, + "textR": 0 + }, + "comment": "Change position" + }, + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [ + { + "type": { + "value": "BuiltinCommonInstructions::Or" + }, + "parameters": [], + "subInstructions": [ + { + "type": { + "value": "VarObjet" + }, + "parameters": [ + "Object", + "__ShakeObject_PowerX", + "!=", + "0" + ] + }, + { + "type": { + "value": "VarObjet" + }, + "parameters": [ + "Object", + "__ShakeObject_PowerY", + "!=", + "0" + ] + } + ] + } + ], + "actions": [ + { + "type": { + "value": "SetX" + }, + "parameters": [ + "Object", + "+", + "round(1024 * Object.Variable(__ShakeObject_DisplacementX) * Object.Variable(__ShakeObject_PercentTimeElapsedThisFrame)) / 1024" + ] + }, + { + "type": { + "value": "SetY" + }, + "parameters": [ + "Object", + "+", + "round(1024 * Object.Variable(__ShakeObject_DisplacementY) * Object.Variable(__ShakeObject_PercentTimeElapsedThisFrame)) / 1024" + ] + } + ], + "events": [ + { + "type": "BuiltinCommonInstructions::Comment", + "color": { + "b": 109, + "g": 230, + "r": 255, + "textB": 0, + "textG": 0, + "textR": 0 + }, + "comment": "Save movement to calculate drift" + }, + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [], + "actions": [ + { + "type": { + "value": "ModVarObjet" + }, + "parameters": [ + "Object", + "__ShakeObject_DisplacementTravelledX", + "+", + "round(1024 * Object.Variable(__ShakeObject_DisplacementX) * Object.Variable(__ShakeObject_PercentTimeElapsedThisFrame)) / 1024" + ] + }, + { + "type": { + "value": "ModVarObjet" + }, + "parameters": [ + "Object", + "__ShakeObject_DisplacementTravelledY", + "+", + "round(1024 * Object.Variable(__ShakeObject_DisplacementY) * Object.Variable(__ShakeObject_PercentTimeElapsedThisFrame)) / 1024" + ] + } + ] + } + ] + }, + { + "type": "BuiltinCommonInstructions::Comment", + "color": { + "b": 109, + "g": 230, + "r": 255, + "textB": 0, + "textG": 0, + "textR": 0 + }, + "comment": "Change angle" + }, + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [ + { + "type": { + "value": "BuiltinCommonInstructions::CompareNumbers" + }, + "parameters": [ + "Object.Variable(__ShakeObject_PowerAngle)", + "!=", + "0" + ] + } + ], + "actions": [ + { + "type": { + "value": "SetAngle" + }, + "parameters": [ + "Object", + "+", + "round(1024 * Object.Variable(__ShakeObject_DisplacementAngle) * Object.Variable(__ShakeObject_PercentTimeElapsedThisFrame)) / 1024" + ] + } + ], + "events": [ + { + "type": "BuiltinCommonInstructions::Comment", + "color": { + "b": 109, + "g": 230, + "r": 255, + "textB": 0, + "textG": 0, + "textR": 0 + }, + "comment": "Save movement to calculate drift" + }, + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [], + "actions": [ + { + "type": { + "value": "ModVarObjet" + }, + "parameters": [ + "Object", + "__ShakeObject_AngleTravelled", + "+", + "round(1024 * Object.Variable(__ShakeObject_DisplacementAngle) * Object.Variable(__ShakeObject_PercentTimeElapsedThisFrame)) / 1024" + ] + } + ] + } + ] + }, + { + "type": "BuiltinCommonInstructions::Comment", + "color": { + "b": 109, + "g": 230, + "r": 255, + "textB": 0, + "textG": 0, + "textR": 0 + }, + "comment": "Change scale" + }, + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [ + { + "type": { + "value": "BuiltinCommonInstructions::CompareNumbers" + }, + "parameters": [ + "Object.Variable(__ShakeObject_PowerScale)", + "!=", + "0" + ] + } + ], + "actions": [ + { + "type": { + "value": "ScalableCapability::ScalableBehavior::SetValue" + }, + "parameters": [ + "Object", + "Scale", + "+", + "round(1024 * Object.Variable(__ShakeObject_DisplacementScale) * Object.Variable(__ShakeObject_PercentTimeElapsedThisFrame)) / 1024" + ] + } + ], + "events": [ + { + "type": "BuiltinCommonInstructions::Comment", + "color": { + "b": 109, + "g": 230, + "r": 255, + "textB": 0, + "textG": 0, + "textR": 0 + }, + "comment": "Save movement to calculate drift" + }, + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [], + "actions": [ + { + "type": { + "value": "ModVarObjet" + }, + "parameters": [ + "Object", + "__ShakeObject_ScaleTravelled", + "+", + "round(1024 * Object.Variable(__ShakeObject_DisplacementScale) * Object.Variable(__ShakeObject_PercentTimeElapsedThisFrame)) / 1024" + ] + } + ] + } + ] + } + ] + }, + { + "type": "BuiltinCommonInstructions::Comment", + "color": { + "b": 109, + "g": 230, + "r": 255, + "textB": 0, + "textG": 0, + "textR": 0 + }, + "comment": "Decrease change the second half of the shake (return to original position)" + }, + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [ + { + "type": { + "value": "ObjectTimer" + }, + "parameters": [ + "Object", + "\"__ShakeObject_ShakeTimer\"", + "Object.Variable(__ShakeObject_TimeBetweenShakes)/2" + ] + } + ], + "actions": [], + "events": [ + { + "type": "BuiltinCommonInstructions::Comment", + "color": { + "b": 109, + "g": 230, + "r": 255, + "textB": 0, + "textG": 0, + "textR": 0 + }, + "comment": "Change position" + }, + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [ + { + "type": { + "value": "BuiltinCommonInstructions::Or" + }, + "parameters": [], + "subInstructions": [ + { + "type": { + "value": "VarObjet" + }, + "parameters": [ + "Object", + "__ShakeObject_PowerX", + "!=", + "0" + ] + }, + { + "type": { + "value": "VarObjet" + }, + "parameters": [ + "Object", + "__ShakeObject_PowerY", + "!=", + "0" + ] + } + ] + } + ], + "actions": [ + { + "type": { + "value": "SetX" + }, + "parameters": [ + "Object", + "-", + "round(1024 * Object.Variable(__ShakeObject_DisplacementX) * Object.Variable(__ShakeObject_PercentTimeElapsedThisFrame)) / 1024" + ] + }, + { + "type": { + "value": "SetY" + }, + "parameters": [ + "Object", + "-", + "round(1024 * Object.Variable(__ShakeObject_DisplacementY) * Object.Variable(__ShakeObject_PercentTimeElapsedThisFrame)) / 1024" + ] + } + ], + "events": [ + { + "type": "BuiltinCommonInstructions::Comment", + "color": { + "b": 109, + "g": 230, + "r": 255, + "textB": 0, + "textG": 0, + "textR": 0 + }, + "comment": "Save movement to calculate drift" + }, + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [], + "actions": [ + { + "type": { + "value": "ModVarObjet" + }, + "parameters": [ + "Object", + "__ShakeObject_DisplacementTravelledX", + "-", + "round(1024 * Object.Variable(__ShakeObject_DisplacementX) * Object.Variable(__ShakeObject_PercentTimeElapsedThisFrame)) / 1024" + ] + }, + { + "type": { + "value": "ModVarObjet" + }, + "parameters": [ + "Object", + "__ShakeObject_DisplacementTravelledY", + "-", + "round(1024 * Object.Variable(__ShakeObject_DisplacementY) * Object.Variable(__ShakeObject_PercentTimeElapsedThisFrame)) / 1024" + ] + } + ] + } + ] + }, + { + "type": "BuiltinCommonInstructions::Comment", + "color": { + "b": 109, + "g": 230, + "r": 255, + "textB": 0, + "textG": 0, + "textR": 0 + }, + "comment": "Change angle" + }, + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [ + { + "type": { + "value": "BuiltinCommonInstructions::CompareNumbers" + }, + "parameters": [ + "Object.Variable(__ShakeObject_PowerAngle)", + "!=", + "0" + ] + } + ], + "actions": [ + { + "type": { + "value": "SetAngle" + }, + "parameters": [ + "Object", + "-", + "round(1024 * Object.Variable(__ShakeObject_DisplacementAngle) * Object.Variable(__ShakeObject_PercentTimeElapsedThisFrame)) / 1024" + ] + } + ], + "events": [ + { + "type": "BuiltinCommonInstructions::Comment", + "color": { + "b": 109, + "g": 230, + "r": 255, + "textB": 0, + "textG": 0, + "textR": 0 + }, + "comment": "Save movement to calculate drift" + }, + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [], + "actions": [ + { + "type": { + "value": "ModVarObjet" + }, + "parameters": [ + "Object", + "__ShakeObject_AngleTravelled", + "-", + "round(1024 * Object.Variable(__ShakeObject_DisplacementAngle) * Object.Variable(__ShakeObject_PercentTimeElapsedThisFrame)) / 1024" + ] + } + ] + } + ] + }, + { + "type": "BuiltinCommonInstructions::Comment", + "color": { + "b": 109, + "g": 230, + "r": 255, + "textB": 0, + "textG": 0, + "textR": 0 + }, + "comment": "Change scale" + }, + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [ + { + "type": { + "value": "BuiltinCommonInstructions::CompareNumbers" + }, + "parameters": [ + "Object.Variable(__ShakeObject_PowerScale)", + "!=", + "0" + ] + } + ], + "actions": [ + { + "type": { + "value": "ScalableCapability::ScalableBehavior::SetValue" + }, + "parameters": [ + "Object", + "Scale", + "-", + "round(1024 * Object.Variable(__ShakeObject_DisplacementScale) * Object.Variable(__ShakeObject_PercentTimeElapsedThisFrame)) / 1024" + ] + } + ], + "events": [ + { + "type": "BuiltinCommonInstructions::Comment", + "color": { + "b": 109, + "g": 230, + "r": 255, + "textB": 0, + "textG": 0, + "textR": 0 + }, + "comment": "Save movement to calculate drift" + }, + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [], + "actions": [ + { + "type": { + "value": "ModVarObjet" + }, + "parameters": [ + "Object", + "__ShakeObject_ScaleTravelled", + "-", + "round(1024 * Object.Variable(__ShakeObject_DisplacementScale) * Object.Variable(__ShakeObject_PercentTimeElapsedThisFrame)) / 1024" + ] + } + ] + } + ] + } + ] + } + ], + "parameters": [] + } + ] + } + ], + "parameters": [] + }, + { + "colorB": 228, + "colorG": 176, + "colorR": 74, + "creationTime": 0, + "name": "Stop shaking", + "source": "", + "type": "BuiltinCommonInstructions::Group", + "events": [ + { + "type": "BuiltinCommonInstructions::Comment", + "color": { + "b": 109, + "g": 230, + "r": 255, + "textB": 0, + "textG": 0, + "textR": 0 + }, + "comment": "Stop shaking when the duration has been reached (or if the stop shaking function was called)" + }, + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [ + { + "type": { + "value": "BuiltinCommonInstructions::Or" + }, + "parameters": [], + "subInstructions": [ + { + "type": { + "value": "ObjectTimer" + }, + "parameters": [ + "Object", + "\"__ShakeObject_DurationTimer\"", + "Object.Variable(__ShakeObject_Duration)" + ] + }, + { + "type": { + "value": "VarObjet" + }, + "parameters": [ + "Object", + "__ShakeObject_ShakeInProgress", + "=", + "-1" + ] + } + ] + }, + { + "type": { + "value": "BuiltinCommonInstructions::Once" + }, + "parameters": [] + } + ], + "actions": [ + { + "type": { + "value": "ModVarObjet" + }, + "parameters": [ + "Object", + "__ShakeObject_ShakeInProgress", + "=", + "0" + ] + }, + { + "type": { + "value": "ModVarObjet" + }, + "parameters": [ + "Object", + "__ShakeObject_ShakeCounter", + "=", + "0" + ] + } + ], + "events": [ + { + "colorB": 228, + "colorG": 176, + "colorR": 74, + "creationTime": 0, + "name": "Correct for drift and reset drift tracking variables", + "source": "", + "type": "BuiltinCommonInstructions::Group", + "events": [ + { + "type": "BuiltinCommonInstructions::Comment", + "color": { + "b": 109, + "g": 230, + "r": 255, + "textB": 0, + "textG": 0, + "textR": 0 + }, + "comment": "Move to correct any drift from previous shake" + }, + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [ + { + "type": { + "value": "BuiltinCommonInstructions::Or" + }, + "parameters": [], + "subInstructions": [ + { + "type": { + "value": "VarObjet" + }, + "parameters": [ + "Object", + "__ShakeObject_PowerX", + "!=", + "0" + ] + }, + { + "type": { + "value": "VarObjet" + }, + "parameters": [ + "Object", + "__ShakeObject_PowerY", + "!=", + "0" + ] + } + ] + } + ], + "actions": [ + { + "type": { + "value": "SetX" + }, + "parameters": [ + "Object", + "-", + "Object.Variable(__ShakeObject_DisplacementTravelledX)" + ] + }, + { + "type": { + "value": "SetY" + }, + "parameters": [ + "Object", + "-", + "Object.Variable(__ShakeObject_DisplacementTravelledY)" + ] + } + ] + }, + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [ + { + "type": { + "value": "VarObjet" + }, + "parameters": [ + "Object", + "__ShakeObject_PowerAngle", + "!=", + "0" + ] + } + ], + "actions": [ + { + "type": { + "value": "SetAngle" + }, + "parameters": [ + "Object", + "-", + "Object.Variable(__ShakeObject_AngleTravelled)" + ] + } + ] + }, + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [ + { + "type": { + "value": "VarObjet" + }, + "parameters": [ + "Object", + "__ShakeObject_PowerScale", + "!=", + "0" + ] + } + ], + "actions": [ + { + "type": { + "value": "ScalableCapability::ScalableBehavior::SetValue" + }, + "parameters": [ + "Object", + "Scale", + "-", + "Object.Variable(__ShakeObject_ScaleTravelled)" + ] + } + ] + }, + { + "type": "BuiltinCommonInstructions::Comment", + "color": { + "b": 109, + "g": 230, + "r": 255, + "textB": 0, + "textG": 0, + "textR": 0 + }, + "comment": "Reset drift detection variables" + }, + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [], + "actions": [ + { + "type": { + "value": "ModVarObjet" + }, + "parameters": [ + "Object", + "__ShakeObject_DisplacementTravelledX", + "=", + "0" + ] + }, + { + "type": { + "value": "ModVarObjet" + }, + "parameters": [ + "Object", + "__ShakeObject_DisplacementTravelledY", + "=", + "0" + ] + }, + { + "type": { + "value": "ModVarObjet" + }, + "parameters": [ + "Object", + "__ShakeObject_AngleTravelled", + "=", + "0" + ] + }, + { + "type": { + "value": "ModVarObjet" + }, + "parameters": [ + "Object", + "__ShakeObject_ScaleTravelled", + "=", + "0" + ] + } + ] + } + ], + "parameters": [] + } + ] + } + ], + "parameters": [] + } + ], + "parameters": [] + } + ], + "parameters": [ + { + "description": "Object", + "name": "Object", + "type": "object" + }, + { + "description": "Behavior", + "name": "Behavior", + "supplementaryInformation": "ShakeObject::ShakeObject_PositionAngleScale", + "type": "behavior" + } + ], + "objectGroups": [] + }, + { + "description": "Stop shaking an object.", + "fullName": "Stop shaking an object", + "functionType": "Action", + "name": "StopShaking", + "sentence": "Stop shaking _PARAM0_", + "events": [ + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [ + { + "type": { + "value": "ShakeObject::ShakeObject_PositionAngleScale::IsShaking" + }, + "parameters": [ + "Object", + "Behavior", + "" + ] + } + ], + "actions": [ + { + "type": { + "value": "ModVarObjet" + }, + "parameters": [ + "Object", + "__ShakeObject_ShakeInProgress", + "=", + "-1" + ] + } + ] + } + ], + "parameters": [ + { + "description": "Object", + "name": "Object", + "type": "object" + }, + { + "description": "Behavior", + "name": "Behavior", + "supplementaryInformation": "ShakeObject::ShakeObject_PositionAngleScale", + "type": "behavior" + } + ], + "objectGroups": [] + }, + { + "description": "Check if an object is shaking.", + "fullName": "Check if an object is shaking", + "functionType": "Condition", + "name": "IsShaking", + "sentence": "_PARAM0_ is shaking", + "events": [ + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [ + { + "type": { + "value": "VarObjet" + }, + "parameters": [ + "Object", + "__ShakeObject_ShakeInProgress", + "=", + "1" + ] + } + ], + "actions": [ + { + "type": { + "value": "SetReturnBoolean" + }, + "parameters": [ + "True" + ] + } + ] + } + ], + "parameters": [ + { + "description": "Object", + "name": "Object", + "type": "object" + }, + { + "description": "Behavior", + "name": "Behavior", + "supplementaryInformation": "ShakeObject::ShakeObject_PositionAngleScale", + "type": "behavior" + } + ], + "objectGroups": [] + } + ], + "eventsFunctionsFolderStructure": { + "folderName": "__ROOT", + "children": [ + { + "functionName": "ShakeObject_PositionAngleScale" + }, + { + "functionName": "doStepPostEvents" + }, + { + "functionName": "StopShaking" + }, + { + "functionName": "IsShaking" + } + ] + }, + "propertyDescriptors": [ + { + "value": "", + "type": "Behavior", + "label": "Scalable capability", + "extraInformation": [ + "ScalableCapability::ScalableBehavior" + ], + "choices": [], + "name": "Scale" + } + ], + "propertiesFolderStructure": { + "folderName": "__ROOT", + "children": [ + { + "propertyName": "Scale" + } + ] + } + } + ], + "eventsBasedObjects": [] } ], "externalLayouts": [] diff --git a/examples/starting-card-game/assets/Background.png b/examples/starting-card-game/assets/Background.png index 83470d7ee7b6b56c474728e849f9b61fc8860c4b..313afed01e19a3496c7e7147c0c31af83fba1bd8 100644 GIT binary patch literal 381 zcmeAS@N?(olHy`uVBq!ia0vp^4Is?H1|$#LC7uRSoCO|{#S9GG!XV7ZFl&wkP>``W z$lZxy-8q?;Kn_c~qpu?a!^VE@KZ&eBK3|DzL`iUdT1k0gQ7VI5W_oVoyp7Y6figcl zT^vIy7~kI6D9Y$4!g|48%RxzDfsv_(<5CuFg$St)+0OzFpLti9Z}fZdcHTYHcU9Xj z>v&zZ+0MoF?&A7m5~n}u?vMSaw0!@)!v%e(+P)W@Iv-m1xCUr40wl!cegAjKcHZ$7 zYf`4Qt@^X==gKRGKmWazdu{a!jpuQnHvgadN&2b&sfnNXU2A@lJ`mkqwgfboFyt=akR{00FX$VgLXD delta 181 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 cC+d>sV~9k_S%ymM)$Rf?&^6Yn(!${X52wBoI zfd?WFg^9&txm@1S(P1{5Q7Du`q4@myv!|ygkw~Ocscbfz$Kx4|M!jC&*Vl)`;l|?5 zReWvX?1SWJ^J<~$ArsF-uZ0k)5ux87%G1qVE8|MM9E;Po3?!*c&^WxLo|X|MB9W6X z%@Oub6-@ivv&}y**T!$t7&k}VI+*{i9CIrCcw@-;e z81S9G)QsiW@fZ#mEmozB>v|HQ9O|fbk-0K09+A(|P*)QCK zGX0Md}WOg%x8v)S|fT>&fUv5u%7I7yJwP_disrB^QU1Yb#ClDykNbzqUOU-<>in?L#uUFzy|3^`oM_>*5G^sspxG` z(y6}gx`Ud>mSwvT%9)lG%cEKQO0MPKzw;yIqN3~BU!Fk^@ptKeo+<9Q+KnL%L#38C z%do?OW3!>HOnFfDBBxAN{a1F%qUEb@E`*+GrPD|6;xEB0B3w6Hr2MlY&x5k z#b9?g;_o@zG;J&~i))B>6>)o5t5cGr1Q&weWS1lOew%kXsp%Kp1d_JhmuxG0Fm5_M zNbJ|tS`2N~IP*g&qU&F%wYeCdErrxGyR^mAjfp=6mau@plR~$G7Ik-{?&b zftJo!L;pAm>Fr4=v(>~V@25)xlRfqP0<5yAIYpX49FpHe5wI};cu$Pt&p_VXy?R&N%{Xr)A%H zG`(dKW2~p-!%z3(RPlyNDcrZ8j zo3!V}o_>&;n?h^z{}7nZ!N%rgv?;R#t*BQYgti#BmjU;;E EzuRp{$p8QV diff --git a/examples/starting-card-game/assets/card_clubs_03.png b/examples/starting-card-game/assets/card_clubs_03.png index b7599445f8dfbb925235e5ea42de760e5e78defe..334209a239fe18ae80daff8a4b59a9420bf99a43 100644 GIT binary patch literal 2999 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 delta 1404 zcmV-?1%vvx7nci=8D;|j007Xe7pedN06b7kR7JSBxVyW%_4V}~9UY5{i~s-s92^|9 zw6wXoxw*NySy@>yFffskk=xtbsHmuLaB%PM@5jf-Mn*<}fPg5nQ1t)+019+cPE-8< z|NrIx{|n1|HHJ2Rks%j<1nWseK~#9!?VW3rvnmXL!H&IfNB{qSeE}79Yi+C`oHNOX z9mm=2K829PBp`V_95Dw6 zY?`*TI2uWxgwhIF0uH4JgE0Rum_|AqSO<*N{N{wI3Sh)-64sP|hEca!bZMB*4+%9^ zdm397#ySKp{S|XpeOVZ)okZQywb+@eV7$_(N?`n|>fmde2*z2jOnMl`n74ZzyqFZf z55iS)m~OB#;IFCfv#2~UU>eC6FoRu42&0q|!T7;w$bBg!$7=FMPk=r{uV8cz80wc3 z=rh;~hUI{femymRMMp+(&imO*9bQbQmeXID+1B;oXVUjei2-K{bWOg5Y7jfZe;1)UnqBvBvN2Kvq;$dI3J**!i-!Z(Q2Y&KrZ0A!lG~L zr>j2I(l)S16&p3>c7GRp?L!>9?}@$^hVR1gWH3^*j_EId3Vh#}R4@wUfv%#6IAHiZ3?lrLz^?&? zkwVDPo{832YvuP)+<8w303`##(dZoUZQX||mmQO1MEbe@W^*4k}6I^Yq z3k426fAZCTpJBQUm=5kUWm1?Q%@<1$bf$t4rxsD_Y08rlnH1Q6SC!yd{$zr291K4@ z`r3hchY0~Sm&W}x;kq*=Ggs1DGg|j{*Zy9WMw1DwMlu+dz-nN<31JWhVGssk5C&lo z24N5eVGyRhq8H+@|A&w7C&Rz(p zYs%gW@k=P4s~6(n*ktX6I2U?~Djsk1^g^5rJXr-WnR+2GKZ7s`gD?n#FbIP%2!k*P zgD?nxgD~$8=Y!M%qqh>*rC`jILZ#6~*jdKl@`C!Gv?39KKVc~@=fl!yhU0lD8x zFx9JM+L7VlWYLr=_8Q#(sZCv;M%&vAlS*e8w8peoW;hEIQ?Jv&kdS&c1z@J6bT=fW zSybiEN$G6UH213Lw{*P%wFj(!H%;}{O^^tGt71{eFguQb%W*rR?3S8#28R48@!Yvs z-1%5bb{jQK+zexu1^Gh7uk9_YWy*pdHush(n2POsU1=CwzQ}I2e<@%40fRInkz_Dx z7iK@0u-bVIoiYZFrhU;_bfypNT=lk_9Fd5NV{CZ>v^%{Y5^~aOlhH-%j+1j^&_HUQ3%>w6XwD4BdhtIrDPZj3_NL(sBGbx#b)I3F-p($b zhqOA{s|*Z}zRYcWsAN#HNEVOSk%~ z!RmE6vJi$iE#?}&2|{)GU=9zZx4*rAJJGNL7#R<7tpG-BpCelaMn@+gy_ALNqqFvU zOv}Qk$ytYs!XwiRhvaFNKA!nO=;m>T#nSa-+{ycdZH2l diff --git a/examples/starting-card-game/assets/card_clubs_04.png b/examples/starting-card-game/assets/card_clubs_04.png index eb9200d6b23caf5422fa3734397323b4ffcff943..b527701f2e3bb29a624d0685230a9935b2c968b2 100644 GIT binary patch literal 3965 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 E7t$1NIsgCw delta 1342 zcma)6eKgYx0QE7fnTf=Ym{b%U{1lZ{!tfBY7)7Oh_*yjY#5`>J^}(petFZ7S^@@CL zQDfQU^(kLuesVwu6A^F(zPv`XKyXT&B@BQQ6KkqVn0CB~bsIH=-(oQCV4;v$HdWLb0~Co|>A93K{&i zx`0Rw#06=uc2?044RO>4hOSx|K`@?=hZ`YzKwcuxt=@=a*>00dgC>+wx*Nkzsa_DU z%{#i1Fmty1Ho?gpeU90kftI=qP9lQnvPn6Hi%v53jTDCF10I>;F4XJpV|UW)xG zj%ptm_8SnO-&+5N_Zg8`x+Ty%&wSciLA}#RE4yTgrF#Oy3@X}dHh#NUO}w6jZk|Vl z0vN8%bdT``Stqy}Q-HpEIiri(_`~T}SE^})?&`>8jAe6Wdi1jl*(I>tzQBE#d0t#2 zUSeHX+JM{*y<}=RvI;bPsD-ZdT6Q=sW*zZs-j8a6E@jy0RX%e&fv>wc2eoo8D|pNg zE@cW!UKBmV0$ zgUx;WqME*v^A1mZlj|o8x{lOD)bxoT<8g3lbrDo)f3%*f=9TumVZNnjGD`f|S1}68 zgo`s|v($>!xwd)aZ-+e*2O=B;aOwEtVkWtK&lh1Eu`It1URB^+7_f5ClD`RW2D5TRlr{+hWBJsKnf)G5*vgpYj}s^; z>%~llY>q@ABP%}Up8f-+`Sm`yMHTxh!Ahf>qX3bQgP%7=-HoZ3KxV6TssAfz(X;_kGHpzDEO7H{KkbJmy2az17@M$@uI~4I8sq zF=!{G!j~>;k=APdLg?4nE#nkUX=rBMjuU8-P|7tnn;nCDo6d%C^z~dxovO`uLPU9Pk$6B2 znZ_DBf=SjzB%GT1z84w~9n#G!;@<>K00UI-+r%D)QzhWX!C3~)xsnXP1sGu~HZ|jR z>_}u`lStcV^u}2kPL3j<);CgJ3D0Qb*%jy8;Uyg$#EjlZ(uPwtLk=OFpoDh@Nl7|g z=Zf4A7`Bgduuki_6klJA)98T3(w1yk`R{LEE-WN6uS?!R;rU@Qlfr|!v_=J78snH? z&>pcbo{ftzG^_^;wn+3bi6fw(zoH&Y%KN(;Ew)W#aSD3GScatSeJzbOU5S3O=I++B zxO)}Fq_*+|^EZbCOs^J7$2tLgGvDP)`QlDJR-C<*1CoWKg7xjgRfn*f!KDZB!|<6B z>`XWiSwm)^((FvEQXe6 mHt}Y^7jqd6XTOaLCJvnGRL1&ZFZZt=oQjVp&V%btJpUik$a|jv diff --git a/examples/starting-card-game/assets/card_clubs_05.png b/examples/starting-card-game/assets/card_clubs_05.png index dbbdc3a12f991704f626480db764c2d6696116be..c8466073e77705d26ee6bcf7364fc5b757856ba0 100644 GIT binary patch literal 3520 zcmai1c{r49+b3gZvS!5CMpCwsHIjXqGFkG-I?a&rmP}+KgbX6a5}6TFvLt28z6{15 zp~g1$@RTKE$(H@Sd*1i>{`!vZ`{TN=Q+g%@U^4sJnj0|{_lYk81jy46X~@J>mdJg8 zVFhR~-qZnq{T>z{fVuC%WOm!r8M+%TrB>k8rvB~bq4vljEW0yo@y2s9nL8Y?mnL?E_!m<_Do8o3khNC zSb@G;QH9>$|HiB5zGK<6IAVE>I=<|D{qrl-BaOrFlq<>gV*wMkRdF85PsgskQapz)*>)E=#%u8F+P~Ff>9bH{Smz?gs zsW02xr$U-4E3e&KoSSN#de9IipLWmw_`%ZVqS{%jFbl%#z{*`HWop7@ID~<=`ix+t`It;^X6=yqez#6qt~%Ld(f@ygT^i zu`qvq;N-hiH%U$$Uohg1>?x|ATQ6LA`u^a+fU#%)%iz`Yl`I@!K|*VuPhHyhsrBUc z!kC7@gd7PyJPao1#GcKHa(h-)RrNgk=Xa?5S+)|tHkV|uXIj+kqh3$GtJ!d~L$XTB zH*gto%FOL?h?;8Xct+H)g-^5tf%;KxVN{9bM)8-y2zI-J*|MDt407266Pg7Ih=E2^ zKhCkci9+j51*?x=qsUogz=-afcEupKx_eMz7O@+E~Vs}T?PB-jv_V#|Zn~pFz zU|XwwOeZ#zN#tAx!#h2*;eA$rU!S#aw#9s3R$k@(TC9C|2&~4qM~#Q{lA4{pt2T`1=YU(W(mq7kv{l0YJn3MxL@hDbhH{mV+x*jQlqK|CELfaSXcFTWeu zBb1cP7VuOFf~+P#YBTl`^CFDyB*YGNDZgoUTdRxKpb`@jtVhXg4ye$4H+3!Clq?nc zVQ^DfNqIQNlqI0vk@Iacw=TiaiRW-R5Mj%rj4_2@?5_m3zLMkn?QROkP%qYuB%k() zb0C~cwg1;}4~%MDsD-nc(+!q_8hHVP-=`M7PpLV9A!Wx zOY*t$SEjE&!>p%29H8;Alys?_dMpZBg>4f$Czh9k^LW?NzvP;IyXr;YT#vP0VM>bR zaVC&nDWw3F0}1}vT@bgmV(GVNkejNIO`|3$Zsr~Jba(ZB_Y0qT_7Hh+8v&O;D%L{T z>|a=>R39v#Q@ZS!`>FrfE~T==uicgCLnc~p$x}AYnhnbe)HgR0Kc4Lc{8do%J zU=fon24`U&*qPp@ho<+;Kl}|dA&i=_sBm^DOXQ^{FO5kV`Eq-W-iZO>%CWfZ z{w%bFZE|yEG+ZH-S4A(JQ7dwN#3`~Bf-IGT#I&zoX{{Xg6&**PGhwdtpVRPD&pD#B z&>@xe3i8paRYrFN4ZMO4YJO;TOhP|HyTz>gR_GYm4KVjlk}A zh5ZRn>r!Zvk~(*^iDiG$IoWRBpKg!iEg7Ajr8O$m{XrO>SdtCre>W%E<$rU%aly?O z6$slZ5L5Lr4LmnSGALDC)Uy@ufUq;{-#Wo#7{tVB@I(t?iE?#HBryXt$A6BaBBWh8 z%JJqtKkstX;v)MngAMFn!OTsSdNx{c3Nl&37E**-AGs8`Q~u?rZ!1>vFzRj#hN$_4 zfGFb45quH9HeXCYjYNE|?Tyt3ERlCu1@rdhhQlc3ww|9d!h@jBK9C%5TZOcO_F0>V zTZm>1u?kUy?lUXM`FiHnZ|*1AkTd&UY5}eP;eR7(dRcCchlgn4gO(ciE!8S@tLi->OASmwEi6m&zaKOO{+cM5*ysb zKf%t}dlms=3-&@5mj%F#7Tz801=yK@j90d3$mGx59e?!yC%Gn-PyeN*NGEn2V-g^g z|Fb;*PxTy5S-+!ovq4Xl@)u2t_5L{@2QKVSw1S0$hd$1%eg@uQkFJ%)oSj?iooVjJ z<^HWgZiP697xAiT`gDE9s_YPOuwR9S%~luaMd>42PomFx1J{T11MA$qm$6L~ z>Qxrsfi_@!Z*w*#J<83d(z3nL6Tj9$F5VGzU0Quh>*jJ@(?EBiZda_xlSc-B1mW*k z&=h9iX3n{F!7NUGuUX`97J4T4M{-xrfgMJ_k(iz=~gPPWGz+$8()>)6(6 znu+B$21l$EiQ!?7aq+QD*Crs3KGCO6<6)bt#`Sg?8Os-mUp3ze^PE0V|B0=&&b*<| z4t7e2n&jXOot{nU7)kr*IhLXA(@{C&c`XHPG`>97I~R?_h70~Y+ssdAFk^{$SF4Jj z$&Xth8PA-k-+W@Q&B&o%a8A|L%G=iW0WF$B+EEX(sb){g!9gD7t_ublL=|EyS$*`i zYhhof)RF39?YlKIp8gMc(IHT7US0o`ZB0>rr|cZzYTj90-i_KKa$g|m+^5;irh;!8>vwHJyfjP z7wDU{cR*@V?qpN+LvWCyLO5eQzHRsqB&f=;=QWf>nCID+U5+cee1x6WaJA`hS0>+> z7tdqlR^&*yWAM2;<;aOzckff~&>F#(a071Gt3tf#dR*tNI7=vg<$N6pVy-m*>tZUd zSs;@5dx(1Ir+^udN~)n$7Li(Nwuu~1&dIkKDU~%zakm)MF~6x=w zddoE<p4XsashwOw`N0X?34F4g5lcqoiMbKM#|D91rq#3SmYVexggW67_>Mun-Avh2W+BH*7(%C?C2eK!;aHE zNxe$d$wmHYvRCy@;ovtPX$lJLjDcT;XznOyO+Ko&iS-)z^H<^(Vawoc%ZZmq_9giv$Df)M#Y~I3Z{A?d?d84*ucp?@JqBjk4vG zBl`IG?0_NB_fKr!$6NE{$@>LA)aXjS-4_mHc;`536T$JLdsrri!Z+&fM8aoy!k6i3 ztM6o$A-{?sr4owWWP6X7$6vqW9kDB4bLg5xK;v=M#t|@vQQ-bt!;YWf?YlDVrl2`K z`Fx|}hP_A0NE3KM_zN~&33B5>Ea`YVU?|PT9ab%?Nra(A{vLDMrR*F|ir?ML0b)8a z3y=D0C5B4rH}}F1ayTYmIMEHA4YDu*lI(hKOli>?gL!$g00pHJe`p$!Wd9krP5y${ z*VoH(^nM}i?oEzY1GD;aL!dEqa9zAV745&{|Eh6nLe`LDcAud%Ha=OO2n_J6QDHiZ;ZTr3VVLbenhg~A!D2#~ LUNb2+LKFTC8`Yua delta 1529 zcmV8D;|j007Xe7pedN06b7kR7Ff_asU7S_4W0;ySur$xwyEv92^|E zxw)#Us=2wjkdTluF)>a~PTk$zadB~~s;XjQV#mkFe}8|%!onfMN&f%<019+cPE!E? z|No~i|IZI^*LimQks%j<1!qY_K~#9!?VZ_@s~`}D4a_kacFg;~>jC4MwAw5NYYHx= zQsy81#HN9!B~8;bzd!doEn4pPpWnupe}zFNpnpyCp!6xFBt?q@cvAXke(953qCDwu zbEmFWZpDkM9eQsly#kA;p|rwOmB(}#W%tJ{V#bHf%OjUFA*fq7$f zdoZHMFt56Su7PwzI>KFj!0_RpJAanXs*y_v&A06et2^*BRI`bSecHj&LAP|C}8SqTA_A+Ml?Rr8Tt{11nHEpYU?`bq}4S#&zQYeVV^Q+Jee% zGl1Wdi&L_`tLT!sTy%=sRnhs*ilg*EN~d%s_-4l&XL z+JFX!l!tvVTDJ)!02l%HnMZeu)osHdT-C^H4o;2LrNA&`<6!7Ul=7Iq7ZMo_*x#4_ zc9;>4Lb%HCPe*;XVt~-A=x0to0!#YOf|Lx`OKZu0;K^_HtI@QrkzSyOI2FbOdWgEj zQ(-Dhg{d$VrovR13R7V!Oogd1x6FAD=fOneJ)8!kGo@+efQia`I145!@8K+%sMYFF$ zJ>-UeNlG>N(=hx>FzQM+la%+6-RY&|J!FST$$QA(HznjfoG^rl&wDs?MycZa>g+H! z!BVorgy%h+2g8yYQH{@3mA9| z?%vj>B4E;9DcXA(CdOtAay<#u?JYBe!1&csA5@**kdXd>4%6jSYOoiESvbAInjg2?c+jEna#7UZ!o`wLwnRHwi!4lIm7E&<|n6b@ozVe-B_zOmgyX&766!vO*> z9_HDK!G!8Gn7W3A(a0nWtj_k^E5we0r)Dy@7Xjs9V0B5egCR-Bt%S~_kRiQkmD+^W zL7RjG^xD*~gp&2Dn_n-bQ99ZKa`Q}o8Urh2hRnWhZroi0%re8Ei`KpDoWFe=3S*I} zOb5DtGa~7>ELn{}LSgc3Ek&nshgdqBP?$WMZq0jWsaL{49E{oUykR;8X9tABu+>Fa zqz>NDD;S2XQnX2=4&Lf4ylFFJ^3bl1PU9-0I+kUwt)g{(y$LG&Av;frLddRvk0U6X zpwnPeD;1w%SAw}cm3yB=`2dZ&O0-iZ^-Z~ho~yQ22FArHEU?XOAsAO5{bu#+ZElOf zw7{hD0x;x4FuZh7+CCShuBud+zXWrsAMCi)PnkUK)XMTG($f3G{rnBRNcrb~jOX!l f1(csh)BFcpLEe~f+j_VF00008NkvXXu0mjfgroZn diff --git a/examples/starting-card-game/assets/card_clubs_06.png b/examples/starting-card-game/assets/card_clubs_06.png index 29cc931bba6e7b1058dbab7306ce52990d17df32..39322ff6470cf2306619b9e06df9c9941c7b2a4d 100644 GIT binary patch literal 3412 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 mI-hX4Qn92^|E zxw*NyxwyEvPEJlRFfiTS-HnZnadC0*@bIarsbOJZ$H&Kie}9{so0Z*2@Bjb+3UpFV zQw0D2|Jwim#cwbEZ5SbuAs2rIm`OxIRCwC$o!gSLDiDQ3QYJH;Mc@Bfe?UcvD2+Pc z-USy^DQ6{o;m}}rOCFEMBtviRTT9 zQ0V9JLAq6%;zhR(`siM|1B;>2{ZwHp%>NALNfmwG0|s&Odc8<1ZvcPOy~fg)hiNC4 zmnsg^8;nj-C3*2NLre6}7K~|__AXPqE!Qw>7_4E?Q+B+Q)!vuFy z#6BCU+k_FXMIkis4oTiaA^S}S)kR@2XV%&TCbxXgWv>>fE(*h(9bPpJC5uXeEtjT? z!`NOHX7sGS6M8QT+8%!|KphaqPIC6G=Uye-HX5pn&nD{D%kEo`y-M)r=R$RHJ2Oma zUcP0xYY!Z<#nP)pf+iDqI;~*jWM5ElHhJy8*ZMfUm)0;(Y;Erok?@KsT31KAXIr7V z{rwCYjPJ?<3&a@Hwp~o$8S6TvzV8>I>}q`$Bbb7#WxYGPtFTuEAj4CJYY1d~MVe za0Ebg+b{rAHSxwACs5rPm>xJedhjGV(@ptYBGUjiCPIHE-najOvuJD#(>pwG!>nQK z=9y5T{|fsWi;{j|s+1TldE@MF$=Cr+v=Dy^(5TT{1NG?Qdi=8tUVyA7g=R&}@o*8l>03}~8M8d}N?{gvIC1Wl`_0Lq8 z3R7V!Oogd16{f;empP@A$(?Xv`m z46iS6iKrvG(d9?Xvjn3H$Hm|evd$1h zsyTmLvb3a89k>4)A4jy!$j^j$koi1}q9L9Q9c8^3d#i0kzX-lx6a2!dIv&i}fWly4 zxDTd7VTW~@fa(0$Gb<0Xv2K+KvWsw_@S604HdQBsX=+dy2~4XbcR;piUNO_O=P z;84B=RCg|Vi>IjPybBpqnY=V6s4jGvVZwi^jZD;2_z|{VUqeT9)E=Jl;_BQUm;nKLDj8;(P0$_HQQ{8VS<Ir?>Es%f9O+tx=TO%G00000NkvXXu0mjfLhuSy diff --git a/examples/starting-card-game/assets/card_clubs_07.png b/examples/starting-card-game/assets/card_clubs_07.png index e3822efeda93cb8b3ab8b68ea7730f8f6be32bad..2ec41a40fefa16c2cce62927998c10a5f0491823 100644 GIT binary patch literal 3538 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 Xp-iZNosP_amJ4L4helTFVhR5Nr=yc% delta 1605 zcmV-L2Dq?~18R&$ z)PgEoX9ga6Wu=c{hdYPTWLcK|d_5lQ)bx0K{bVkEpA0ereP`K|Fu>_M~63h`WMKw$MT$uYTC73CUl7D?Ft^m{Y zuY_f3&uzd$h@L=O7Il%Xo%8#>$}a-rDoI{s&@<)zUaVci1h04_cgqc`+k1>jM!~w+|!NuYc#WO!(^OLUsEvYJDd7{gUD9Q>2__B0-%A+?{+dx$6lA zQ2tw`&%3}e9$XiZK z{Wq=T*c{|5KM|z^!U($(SAG_UInFuF>s1q>!)#UbeJkl;9SWw{%zs1$>rgP|W+r8@ zE(E4loO3mqEAQJWSQh{z7qjhoF!i!nD_|xSIZV53{%SLi>K3Yl!-();JoEvNo`Yz?BzM0D%HK22AsP#p}$2vHAX zm$>Cx>!Eb&YMg^rKYxBn?^Xn=gTQo!()6Rcl+$4|7k2F7a#;D7_6l9tH^k@42ddkL zk)A#vy-Uj7IT);4h2b6;?w>OaaBelxFo2=z#arxrG*pLyX@Q-i1$QD$HPJW@Aq}|m zXTTec2*&}`wfRsOu67i#U}78x5KOmtF6-R>&AZopx9FoB2Y=vnP-~qJBl%5hKA!>Do2-0P>OOK4m)q+G_^zn6XQ70!yp_7dKiS`Kn{a( z9H?PXjsrCe%5k8EK{*c8FsM!bKMhk|2?pgjP{W`c2l7D=<2aDRU>pbfxe4Jo9I%9l zcN~rk=27u;HGerwxZ`jjOt|B4988PqL@1cwj)fp_9LQoJ2pk8xSO~o1Ko|>wcO2+q zAqe)>bSM4hpD@e{kprCKv(^10!u{4=!Vnx%H2yaSUBSud_>7qKoiKXd)dOS z1vek(P=CK}qix*`6Vql2;90d-w{@ft0^?3X?fr7^J0y&ILx$;SU<@BCSoJMoxZU#O zDN8##l4^R}@Xe;@f$IpSgN^Elf* z;#twvqRofeDsFi@Iee!%{NkZH<&D^Y!f@bmAAd~8{D$>82d3}ap2hhvEAtl3>1`FL z%l!w1G~owDs7`vr)SxgD7;pjA>F)6gv1{P5o4orA_r+U4b-2@8{yO!1uR?}Urdb#h zREONEFn8*i&rjG|`yPwb-5UDVx0}u=|jV5StI{rBygu-ZG*b+on z#eYr*2ZU3a#WwE5KvTW=M<`6OGV(^~+C`Ct!nCtYT8vIbuV-c(_HkjEV|3NxAHiK) zOhbn-I!$4T2;iW%r}I2=GGa$rH=U25G)m{TH2f-_Uh4^a!GOS)2=FCyP>#^>r?c8&2=a*JG=%0OOlR6HKk=DV?e$n12W6Qa{*n zsh=`=KA1M=qZrK|Py6#X>>}yg{TR>l>k24e&n)``hVuNo8!z)$00000NkvXXu0mjf DtE&a- diff --git a/examples/starting-card-game/assets/card_clubs_08.png b/examples/starting-card-game/assets/card_clubs_08.png index 54f2ab5808bd355b8e826485d77786967c764427..28d46df4b5bca3bb19f468425e8781c5a4f7bae6 100644 GIT binary patch literal 4075 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 P00000NkvXXu0mjf%?owz diff --git a/examples/starting-card-game/assets/card_clubs_09.png b/examples/starting-card-game/assets/card_clubs_09.png index 48facac4f89b737bb8fd8b7c4d04c03bfb1c5a50..d30db4f2715f797160c75882113223dc28cf1d1b 100644 GIT binary patch literal 3992 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 JNCg<;e*kqM#|r=e delta 1888 zcmV-m2cP(+BY59ySuyf_4T;8xVgEx|NsBF zxw*Nyxw*Ny9UUEzkdQw=KXGw!R8&;(@bIarsVgfhVq#*)$H(I0;(&mF)z#I*!^26x zbdLZ4019+cPE!m2|Nn1a|GVFhSvnT>00009a7bBm000XU0Ff>Ve+IKjL_t(|+U;HI zdZQ{7P3TNI4In5;-v3PxAR1y2H$ueIPkyAST@5S9zOdZl<#M_F{r>(&RhuU8_hQ>W zbcptE;POyXepPK!s)u3ogC6Yb0F6D+j~*C3D!1xQkB%N1gpR<)utDe~bCNm9>_LVp zR+EfGm^SV`Z|?Uyf5ElL^yF^(AAZ62q0w4o?oTp8zw2VTlQ3^KgE4y}z3 z7ZjI6hF_m8)(aGuLq@hRGOE&G+e=}|R(#dk1fa0>8Uh~pP zv-r7C9GHv?2iOk>yN-+$+>qf&je$~AS}>-VnLu%XsOTrFSx|Yh)q0 zV%^k77xS%9Tw2;W`Ur+F#@e?t;`$)E53Y~R>=ug4O2~(AO87J#_}Cxbgv%i_je0l5 zoPK=QZ@}e{e-YPM6Un$NGF`M2nvBaL(?&aKlW{3z6xDeV^)laUyd~oj$ndvlTaS!- zA65eFgcK)(-={x~_OTR5mvI}&JR_v?J`x*~A-#X(mWqRud8TPH@ReIjmU>4>Fn5UK z;1ig>hyE$l=&y(EtDHI+tWE-PNs`27`?#=QITxhPt(F6WDw_hc7f}+5!n`+Qbcp zIXe_Ge=yRf-{ZgCluo^ik+B}MRZPD!`<}4%6p--YL{Ui~xo$!@P(m>0$+h^YptI!) z4~U7Go`iY`vL#hWB-24X#HJ+bQ&&|^GBxK6Sl<=X>fZQ*?!`@((1BOlOy8xWF&pb+ z?b8pJ(ScXE%o80vbg%3(OBe=}mD7Ra&i`HCf0foVHiJUNT$)-+2aeB|1$~##elMj1 zgTnJHNV~~GitE79wxI9oQ)L?~tpjfvnU5*tU}O5Y4xDtJKCJ_cE@Q%RO}9gOjPJ1Fr_YJ$2wftEU6Uhh}dbI24+I`fgbrc=1F5_1%&>aAa(N`ffQL ze;A&6;$pZQGEjXtUk6S|Ji9`;95PUSw}1|OJL`c@GAEgn%t_|3WH4}j2*u@%3;_l} zvO}OeRtn+P)3g|F6B(ld={kw$l`&}!@XfdkGLlRp3ms;Ho1#Q)!j)O7^z$W1t2W@u zC|CMQ0JdE{mx}%cUMQ~A@)nulCuntfe;dPnZsC6?$++JmiJZ#Sw$ntCaUYakyJTDz z84eVip}0J!H;unn(iGB~p|}#^Kr24|>DP+VrzMn>^ruj+!)h0sP^iF}3v zuLMAGIb?h#wJ(6nAtQj+AyTa9S6oUy)J66#>`~?1-FGRTcPkmZPy&Z%7t`^Ue^R+r zGVG-kg}ojp>$tL}jptt&Y5BVo_Bgj%c*ldd53jYU=TDHSxf0C#1Kz>u>72Varb4Lj zqjc+4rn;ec!R=cbNBrh6r!PSZ ztDQ=R#70fk%uJWfK+2j}8weAHf23EQSjBl8i_||r1|<|uD(jb(_1MC=$wrmcfk_~Z zTF`;rB(wFyE(M$FP|N7RK@StwfyYGM%;0{Ou$R$+n^1xo)`4R;!<5m1!#7(6b>Nu8 zE~En!|2sx?;4);Q`Yxl^Z`e>!~l1*nyDU>Xb`b#!19F50Lgy)N9)SDTEeuLHAiep*Qf=E01kmJTeamK>=# zb>LG6{=ezKa(P$BopsxsZi*U>Huiy(ODre>X4thJHW?2I2VqZkN~Q=T`wTP<=Ou8#gw4jGNy#myuz|Aj9-CGQN-Vz>e?BpW{5r%K@KE_AR9Ov;j`k^yNp!0LN aT>b+H&nr-?RC_Z30000vxt-gq= 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 cJob|AfsURd9uVbWLi*StoKW>yg%K!iX delta 1167 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 delta 1277 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 diff --git a/examples/starting-card-game/assets/card_clubs_Q.png b/examples/starting-card-game/assets/card_clubs_Q.png index 92b1fb4dee5328c713a73719cfca9d9a44c3b3b3..482d7b76dd4654011977f8e1f0e925be4db2dc21 100644 GIT binary patch literal 3728 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 M07*qoM6N<$g0y?dQ2+n{ diff --git a/examples/starting-card-game/assets/card_diamonds_02.png b/examples/starting-card-game/assets/card_diamonds_02.png index 09674eb4f9314427688b54db126e3a3c949d83d0..8a8c24b9eee3aaf3d5160856db8bfb0849a8e42b 100644 GIT binary patch literal 2678 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 A?*IS* delta 1082 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 XH`!)W`Vt)YV1Y1}Sc?+#^RfQ{nNO45 zm&*t=ah(K4V155~FBm1y3NS!YWVZ8U&)zkz1fxkFSvzuy?UU=0r8W>-`odBtx1P}Fnz<=s3Uj5Wr< z3#NY}cpugg#0QvdaZV54mn@V1%o1rdX4PJT(rWK(N&?s|U(KP#%zH4FY*bi|2515N z`s+cJRe_^fwB6)Gc|)$2v?;u;txF$Ol*z!fZK=M8xUl3bpu%`xo$k6*Uk*l@B2U{4 zi^VJlTP7$cfe{#i5g36H7=aNOfoU@s2!=`v(PCs;hz6t6 zLNu_@X(6hOTnlk&dLtLuVd%9G;jZzEI6TJ9Zi25o(sfDPo z0;F1q){XRkXdxQX9hnxQD}Ir+5M6&e8(Rxe-#pN1A$qC-wico@)hNcQO{#_H3}RsJ z^yst@eOn4w3(;4jkhKtfLF|Wkq9oEnbZjXY<-wmy3(;B^w47WCjKBztzzB@M2#ml8 zjKB!YKM!-9E#rep%$Bjiq-M+5U{bSXTrkPmGA1oVdbW&53z3;EW6?t7X3Kv#v=G_Z zG7c?7cD9T`3z45KW6(n6XUiD05c%0M4lP7>wv0s!k(({!(L!Wq%b2tf>De+aEkts* zj7tlVoGoM1LZoKP__Pp-*)m2g#BsJvv=F&VLfEtr_P)fy^J^hgNP+WcAwK>EgWg=V zXd&)*5b2Ble4inY7Q#JaJYau!di7ce^Yn)eApoxy!lW0IM+@Q7tI4B<*za9aqi7-a zFmsQKFuAo5-Vrocty&0Lm^v-Q=12qfOBcbTg;42O=Q+8`+?L7%^SUinZN?7+$Mnay zOnW`B*j2m5?w902pg1h*X*+^uKL>vFoyMMqcz0keSg++3kHGvcn4vX3*fG?nOrmRM v#g7s{^0IE9znKTI^ZqrS+XyJPX`223*`E0)Y|5Jo00000NkvXXu0mjf^F%kq diff --git a/examples/starting-card-game/assets/card_diamonds_04.png b/examples/starting-card-game/assets/card_diamonds_04.png index 6188fcce0e54da5b2777dc5231f36f1330db934e..d8eaedf279ed35659444ba1c87f20615fefa1a12 100644 GIT binary patch literal 2971 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 diff --git a/examples/starting-card-game/assets/card_diamonds_05.png b/examples/starting-card-game/assets/card_diamonds_05.png index cf564a1cda5517b1ff4b697d8619f136bb5e345e..08adc532518caf98fa70f7929b82cac904282aec 100644 GIT binary patch literal 2935 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 delta 1359 zcmV-V1+e<}7V8R-8B_xR007Xe7pedN05DKYR7I+)s=2wjxw*Nyxw-%U|H(f;ySuyf z_4VC|h|Nw;`}6bEW@hu=-tfxG=BB3Wxw-x)RLTGV00eYWPE!QWOPZxs%p{Q^7k>oN zNklvF6x2!P#-7IX9c-?nDAb$2U?K*))6Cj4n=5O=SgGF^1tbO<$*wX}S%UX&se&Yl)*XU4}R?_Y+7Qnhw)p zI!qlHJHKRU4Kt4~X$@n>6T!HZ0e`|n+Qax2COm&@Z!^okzdLD|_2!KJOfb&)#hct= zKs-H^!9cW`hkHXB2S(aesCp?F`*LjS5(kCN$n!$g%fncBjtzc{f1n;47F90|Gyix~ zyj>$YH10V^&+g#VRK46hRiIeq6Q5zEX2{xO81LLLXk$+$Uej0cS zT|i=?)9hi4aT%Cpqyg8el@1bT!{v-2wja3ylPO2Vd zp0Un2z)oSuzhcp|QuQdYX~jTqHH>?K1LNfw@!o*Lc6>nmYcB!yLy8{7wf#>lSkT#h zFJ7oeoM(*l&Kx*M(p9K>MSuGyJEp{n^f)k{I{JUdpQ^`zar16eL*g1is7Hp0v(|2w z0rVO`@@$fCCtxggj}2xH$O3_?M}z_VpD}w!LFaj@9tmbiMO(MK2=1;_JpznNpJVxR zjfJ#=pspbhnD<$0=Tbm4N?->jd~s#7c4vvRnB|6S6bAAh`No=c5Pzy3VF{6$tG2s% z5NoJ0yP zUT)G`4ks%^TkVtea^V2j8`GLk$SrTOstfZBySy>3_wFFN6yw243=XCro(iY4kHpc5e$^?%++AHc@H&V*m(~XVfc9u^!J z`Y;7~4;5@ip7&70o5Xn!HDJVf4^<{TY2HJfa3IWks1&tjd4CVJ)-#|In6y@URwHC< z!ASBR>Mh6xc@Gu$O};2vYwsvS$vQ@TuMGNwu}d())xY=En~qb_Ju%e%Q!HaeId}=G6sxpUkI$Wj1HsS z7Xqm*YuFcJUt30pY1kK{An$>yw{H@{@g~*25X(br27fPv(d-KmFi6&ESh?r4ZsV{`z80`BWx$7C7z7Ve-T41e}FFRHGLIA#pVZ*5Og-E;v z=P$@L`a%RP7>&LVeseGY2&QC5sn8c9j+*txMU}a1uU+03VnNMY1f$RwBB;S=^o8(X zXA9BF;czUEUfp;i7+(-5)fhtXNSoV6Fza}jD4%U^o55hKixa?@6T!@_A4>ao>92S? z%>M*)jt_R+pC9bFp6630>sMaZ56-yuDC(@Es{HJ;lUP;SF8d;@~~*Bgc3 RXBPkf002ovPDHLkV1gF9g?|75 diff --git a/examples/starting-card-game/assets/card_diamonds_06.png b/examples/starting-card-game/assets/card_diamonds_06.png index 2f58cf66d902c9fec9b48f1c18e8905b64da0698..a522b3ac32e757b7d34792c5703437ba69fbde30 100644 GIT binary patch literal 2855 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 diff --git a/examples/starting-card-game/assets/card_diamonds_07.png b/examples/starting-card-game/assets/card_diamonds_07.png index d44b1a4b61e6bc9fe6b704469fa92633c953e6a4..4e2a77271b2b70946a6ddd78da820157b2333fdd 100644 GIT binary patch literal 2890 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 H9*Oclov1S- delta 1383 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 delta 1460 zcmV;l1xxyl8P5xl8BhZN007Xe7pedN04-2VR7I+)s=2wjxw*Oj|NqH9KfAlT_4W1p z^781euHK7_(_vxEOH1?I-0;cC*mQJ4xIvTv000AYQchC@#Z^%A>T;1G7k>pgNkl@%*QF9605&v_YfFn?~v@_jTrFnUCxl?(CDpoGDM*&}N7kQWAH7YrjR-N3xT zm<5A9x=qy3Hf3P^7`Q$bo3YfO*B~aENlVCmote^U%5RG$lP2Clop`5#3heD%WiTv$ z`uBOJQSFW@a9C;{@}=goIJlFKrrMoVV57CgDzKUzhu_6CyR)LFmw(TKT)a9CkMAVA z12Asorunv!{dOj^c^c-IKdD3T9ie03T~&g6NdZI1<3aX|XbJ7a-cDZLUr`ZS>yW zVF2R*+9eh}eOj#4{u&_Tppvk_?ZBkdoym*?y;0hP=Zj3nA%U*9Y4kw`FOy`& zurwwv{Y)sWcAQ{x7>9bwq725NqRhz`b!%?Tl`h?k7k_{;*~|n#7%JmXtcA$cI8d1h zW-wpWLa;Fo3|a^_#(_f%!Nxc+Xd&1b2M#R+7vsR9gjvE3sK5AFl!--7>A$NLS!=z6|@iqj03wCqKI+e)VjOt15KN2%ixz^7ap2HG@G%YyS_nqQfk6vFOsOwvAxJ^| zH7x`M&G)nr**u!R#OPv8Ekri=T15+y%}NJxFx9mX*}r5oEkx5oG%ZBaLNqNz(?T>Y zMAJevErf&?VmDhRsD%J#%fz%0&}^BQ76O_rlYi1efU{*nS_pWyOhyZV%$A90A+Xsp z2`vOVTPC4}KxfMYv=I1gnSd4opDh#6Lg2Gy5?Tm!woF6|fz6i5Xd#f>nMBw0P992p$;+*q-tuyd^?Do6C0@oe`sE))?0dY+UleHo O0000$&giNwP3AU}F|!27y3qh6r6tU?%{pg^3aP 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 literal 1549 zcmZvcdpHw%7{{l@Sd3C)CQ3+@CYjQ)KxA(iw&cX2TK6m#a77HCz+9G`i9PWZ~-6UO$>06(C;-`ht@Rqs?BnOW$ z0AQ2qr;-8O&Q%8hWY3?pwZ=sb&*aTy&~+5!E9-1LWn^d9iK!Kf0{{~+Ysfn~FZ<`> z+2b4L;Qj+<97$+Ru6y7XQeaSo)l72_L#;%jOh<1B3W#(!jiF=IBwg8$0NjV>m3lD zs|(64Ms3vu;SQT$BU?KrraP9!wu#tY_RI`64+NH}gAg zccd$2kTzWMtm*Co$Uu}p#a1a)Wq9#!`MNEiml+Wn1`riqp|^2^2hh2JQNCxE^a{+6 zEDOd`m-VRqQK_x7rlwe=I|_mEG|w2q_1udtQg`HpAXJ{~fH!iLx89#bKJe5B*Yw9_=am*|>)5Zv*iftu2X5idvR58fIvC2z6`bN{`R_K}IMzdWMfD zT|tGM>PAVjZhG4731j<3DAy!Hu$7Nhtgh9JtXfQ3@+zmoGYOQAUx*{F5sUp%+IEOy zYC>&r(%XuGNo(uGW!e6V;pHOxX=oAN-_p6biejg*ltzld?s%Sxkn5irvyOKx_L?0p zPC@|*aoTfx^lnU`jab~5%w0wTUI%$6y_LL~-cBBKO9$$DhI|7_8K6R87G4LQjWdY{ z1spaRF~?)WB2@*PHnNRd9x&G0BLR57$ukakVpe4;l(5Ho4G&T5b&-WAwg*eaFG2|d zi^Xm=#l~42sMV}569-Fz+#u6V!AQYs(dC1oqt|sZ?_5D>f-v&L@si62Lq@Mx_R5KP zM`7wthN3QWBT(D>Jy$R|3?yZiukSMJJIW-T>hy2OAp;BNdB;A{;4@=^26H?@LynZ~ zPIUoIoPEAg4LNvr{1wF0EG0=$G+a}}+W==Y^7o~q=~KKdF!hq;DH5S#e`yQ3zqT3z zvY&c&s zOdWb#uZW`25LC4A_y#9Zu!W7;@tuPnXO23dA}(a*=5+738r(SovfR)49!WE;;Unjp sP3)dp9cJY83d}e@pwOOM4=$|9q#J4kbQ!IgOMhU%Njr>f>2U((PvBYxCjbBd diff --git a/examples/starting-card-game/assets/card_diamonds_10.png b/examples/starting-card-game/assets/card_diamonds_10.png index 995ca18d4e8b24a28fdddbf7998357536f27a04e..1724b1ad8b974dd506e4d0204464b2c58d0ec58f 100644 GIT binary patch literal 3598 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 acDXq4s$+L=4v7Q5YjirA2Ixw4Eb(6&)wBQr delta 1557 zcmV+w2I~2a9Ip(J8EgXp007Xe7pedN076hqR7Ff_asU7S$v;24ySu8Ys=2wj#e#ym zy1Kc!xw*Ny_4W1p^YhJ2O!VE|(qLfeu&~{ShvAl%)^2X{(9rI~!`XX#9XmGw0000A zbW%=J0RR904?oK2yMzx=dPk8V7k>qPNklD z>%U7xT|bIjZD*zAj%jx4TQN;cd4ZUwCWFafGMI0GX+*;brWur$^Y=qnRezOKTwsi> zrtx1tsOlA(3rw|w>Fsx;8STG{2Tb4hW&xuumKQ$Bwt)W+-oB zgXYC7;6~2pvOKO@(7eE{-G6>;?Xg;7JiIbAFXqrQ)f(pD!q7Y@7*{z^)qAMJjReU% z1S9%k8e9!xE|f~YUu<5KOL!M2G!Ig>=fZ8a6r%YY!;a!!oY1^8Fzp=AJEJz_N>GJH83v}jCTJr z&3>b-=e$0s8o+ws1fzVnS?mT8PQH7P7YauBuc3U4a>I93`0?uo^L}dWwv_o8!+80w zCofnMk%#N9bDd%izJJ?=oOWI?`gljaVO>Xd@LgwKC>XO{dd63y-1o#B8F#Ol&ucs9SOa;n4wT>=Tf0AHM0WPp2|M7s zA9=__Pn{lCjch=Cw+X`HE(aQ3Y0-QaUMMB>^97#o!oj?@)PMOLNATSMJ~NF1xL^d| z4WXS1bmJrVZV(|iph}D9yA*%oLG#^&V00)iCIkcJyYV`3puP*`#YAABd^d#-T!!kq zP+m*`2FiES=)fld<-2Kg;NyVu-9$QYz>%v$d2v!OP`;Z+2R>Q6K)#zu2R;rc-zCz4 z;rcF=7iS$f>wm!C(1Fo>mp})O;Je9n;3&SES_eKR4=0jIVjUP-4Ls+3QXSa8y@C2} zLLE3@5v$0&I6ec_cN6Kr0cT&AATJ6GRNqaa1H)_7Nv%nt1H*NYAyHo3CrYTkn?MH! z)&-p~FA5A)-%X(dLq=(m=4GdJ29v>LFle9emv!Kh)_)J2&gaXZ1DBM3;4+cVmq`aM z>HNUqI&ewn2S(_?C7B->qXU;DeqgK)TvGUf(K>KR;0H$Qz~vYGz$l+@)`7DQoOR%= z1Jmfh`a(J|l^+V!>%iF$e0@Lg!BZW-?FTj!&hEK-C6WjC6jXl00000NkvXX Hu0mjfbesZ1 diff --git a/examples/starting-card-game/assets/card_diamonds_A.png b/examples/starting-card-game/assets/card_diamonds_A.png index 3bd4455a92e0ff9d2d49b668f41fa69fc7188d08..7c0cb9fdf045fd967ac482a38179937f059424f5 100644 GIT binary patch literal 2664 zcmbtW`9BnD7oRbf8DliIv89GEc9T(v7?XX5YrAq$Oj#mj8M2k3#*!r@OBll7st}=E zTMaHs6lG5sYuO@MXXYL6pYXmvoag&J-}9XFe9jNg`JDTI|;mk+FHJk>ChjD?%3XvZf{zjI=$7;WYbXXl4+j!t)cUag`{t-CV*?{ zcJ7m}gRYj?D)mLZYAdx{QPkk)d7mx2+-vN=62R zG5_qCeC_X{PaWSms^{eRrWO|FQC${x$ifR-Wtu2O)Uh?{q&a~5dG%yjl&b3c)P{eb zcRw|vsA*^vO36syQ}d7(DHMsr{q!&_Vq@BW$Mt%Vw#o)2fbg!VUdwmhb?x&}?zv|h zeE7TMfN-A0mnPDgNY4f&B6Jf(j6uM8uK_urSJ%lTK^@y8KeV5W-VDkMU-jbJe4#}* zIQ(s6jws(-mTOS`lcUHHwiafyN7V4l?|j^IMAO{;viB2HuFWh6FhTtdWxuf(eA<@KSwdzho42LBilPQ zvU_6j8EDua*~8`VrjCR|Rd)WejWcdj^%#aID!GXnqPBSTWGVuBYt)3r*IxfFQEX_y zUp!PLAnh(^fN43MrwH5BD85Bl6h(!9YuTgB^2z?o~+e1Fiff zn1$wj%FNj}9RX{*J8n^u`djqI0nrxaTO^FK;6rx=l$LKIYiWHe!MlVca>Dwv9ba#a zz+nY%wxPQpd-GEVH&%y5K0sJA{Z#I4qp?shsQ!hDz=(Gnn^AO_ydG=tod4zKpek$S zzGj>GNy`(%lo{M*^cU~lw&3EO*%KCgPLvsC(mcI+VrtO|SUyyTM#Ph{HurRzS*)oh zo;l22G(z!s55ji~m(U*Ob3mL6B|i($M9HMdmOD^6xno#A66k{)-4 z7;d>RHF9|>ye$$F@U|o%!g<0ramH<4oXrvkHhFZ(eB`17)R6-J3z&SYc4ERZ!cpo5 zB5h(|5#zn0fPHE3Mz!d(nzjejdQl_+Y{<6!njx3Y>klA`h95li>$Gt(82>EyKsiJR zT>2&g{|vvYTttTM2^i-J2I>2r?(spU99vYg%Rd+Yw*Lv-8xyd6iEB#o^ZZo^RXkqD zKLu(^)GZ1M^|7spn`u$#9UCv`e$M*xp|bHMeFOXnKQ#y|JiS_PRO?tE8t*luT(4X( zATL=wn375Vr4_f9b+Cvi$n(=j5nlfaNV;AHs-P@8?dx=v4aYjrR~$JkYd97|AMJDf zX9oA<4TKn}&di_1{lK=M|0yFo&|V34LNjTc)VblP3X`lNw7q^pCKOCz|^v~g38$JhDAhcVt%Pst55{1i)j;hrQc7ib$5qKT> zdO;I#N4M-nUbJW&vG|$tF>5(WWoua6ATEzJqC`nSFZaT9<;$9G#PE&9Amk{LR-Yb^ z-tLrj_cf)UyRHt{x+u|>$f-CAn)4H{sqvsjH+$^Q{p7R4TnUyi)X_J3Q>?7(tFxMD z71=$|?{Ev7W~k8`K5i7sk%Z&h;fDHAA0_ly24ZO^WFd+9PMkTgp{-=m#nXgb8@e5saH=ZA~|W)N|S6?I%Tm%I(<0DD8bpqEDZw| zwYo~1-IW+E3b4>(`NVaFRB8lq1pkvkek?AY#MKGy*b^YfHuEg1-*dxiciq2vhT{pz z(X5M01HB_Iw7c@zTd{+58%~rLRe_Yt*t!M6uy#fU-ke|6#0$U0)O^eWJtx@)h{DDNx5@E@2d zGf%p7kedp#8%UJfaOLv}3&^^hhqd5x!Ad;gp#cAXAa2}m$O_XK+^=F4)I*7sTF#7e zY~9fKx*h%df)w~v;!~YEgF|ly!Qb#?)b^JVX3^PLH12QrRx_~xPdj3Sinzu@Yh(&Q zH=y#CQ0cAZB-9X}Clq`NAb8$bDXHW(OWj>X8k_s5qc~9qHVWy#>c?&(oQ**%S41!w zg@zZ&Y^gB-omSwK_7ds(QURP9TzCGa2jHDd)orK z);kDkhMNI`_!7n@fueM9jF-fEU02?!3|2M;u4@iCd3-qSh$HEI3EPYf&6N`6#OZ1D zn#{aU^w%8fd)XAU^TR<)eN2fG{9z12f@p8EIyi3=<*`(2-i%nvE7GvvTJT{p3qc1W z0|iLJyBRN7!%p??PE)sFs95|(My)pCZKgsc8f>ZTvs;Nex6{%mn~sYcq5r*}?|A1l zr<*5d;NT$kB)t)%_NB`~FJSzJR@7ZI*ECub+#%~%@g>Y{?+j4Z5^(!b-8g<$$h${6 z^boH+7ep=h7;T70b8VQ0|E`eFzR+)2^X|PkiVidoe(V&@Y47t~PKUz_`pwmS6m`cO z)**E7xEXk6E@uMt*uK)CbjDMsf>J^Apa@Z1?#*PFUP2mPA%+l&=E(3LQPPO%Rfa>wnxe5@^m9C**)5#O8^P03e z2t^1yknMjyL6lb(^54}!hUij-0K(0YeF+~L&Tt`<%vxLk6s`!68A<~H007Xe7pedN04GpPR7JG3w7I#txw*Oj|Nr*&^}D;f$v;2N zP*C#S-Q9(S?Y_R$XJ_T4q{jjCbN~PV19VbOQwz&`8TyS5ks%j<1D;7lK~#9!?VZcA z;~)$~b7Nwj|Np%M*pm<=?!stj)s(bJd3P#}ppg-NJRXnd&(F^b8X4gEr~w9f0~MD1 z>zp&t$N(85YxH^<5%GFGvq<@0+0aq`oh(%OV>SU%{u+f*7=`(lFgAx50u%NeRtStQ zFnf>zV=Igo0aI0fTCWogQ&dXpbs}L*Rw=smI*~9%rL@K6I*~9%CAY2DiGryrZQFXC zD40W~t?FdZFo#OVZvev_xs)TU)`@^QXS{RXGguhk!YtLPhcRC)OLgjD>PR^^Rf~1% zVcHy8oKGE0Rq5|jTC39)#>YBPLi>{Gp}$kw#Es6b37#7s)&UNK@NVey2TAWT)YPT>>!p0vm1rO^ph{cIsD9=G0MXcJ!F5mS3;%7=qt+#q3+Zoo^ z$A2;8DQ&ucRZ$p)Q5c0$7==+7g;5xVQJ7od1;P0FojDs!VyL)r!8nd;4in5-?wZ`5 z^T50dfpZl=9vIJ48)bpmcd}C-ZB&n*;@vJp?k|PFoWJQ2+XLr3;{FjEknQzd&>YYtC(OU*I z8fMFg#R(nDmN$h$XB5bM?MY(OzzOgc$Nuogl*L#bb8yBB~IlPF95N zJah`(85fL&sP|r`?i3vcrs@=h)by%O{YfN{(~IO;M-WUR&-8&{40&D*S_hU=t`8+U zbgQB;{~wHu4|Z(F2RlT4%A|a7#^F)i=aVxc;`kWPQvyo)Fwh?+L0d;++lmhW0000< KMNUMnLSTaWMd(2Q diff --git a/examples/starting-card-game/assets/card_diamonds_J.png b/examples/starting-card-game/assets/card_diamonds_J.png index 6f9db895ac4a1c233561bb9873a35baf788c3a6d..252d2495e9b0b4668686b9653acc0d19bdb18fcc 100644 GIT binary patch literal 3625 zcmb7HS5y<+vJOaSp+pE0ktkh)AW`X{v;d)m5;_VdfPhFZqJRPsLV(bFktS6I4ZRpi zkfM~JR6%J1f`XJ^qzJ-|_dc9+*LgY*vuDkGv-YgDXU(2(;>=AAxH*M50RRBEAqt6M zXc9wSv$HbJ<$&cJhT`x;*#`muApXC?1js9ZGMr3-7y}(ZEnWB1g7oZyEPdUALT~}M0Q$Gxy>W%)EEh(OGDD=c)jg&kkIlRw6R_UdS~8_+;us}# zzA}y4oq6(WMwOMPZh0C=Scg|T6#$oD!;Dq2rBvme;6p7%7GtWWB9SQ$%jN-*#&IlY z2|gOf6T7Vvme0mb}c^_m&5D#pRrmV_{)C?H#*e9lHmV zB5<@vieE(F!Oo9bD;dQ(jX#;vYP@{c`NS?M+|1D&nV1N{?QiMn`y_jT2ft`mXN?l* zwZBTMTkR#%4Yj`*`nc)4)}OwV`HmECzZJSMK4T#qA(i8+zaA1C;=8iuB+qVPLee~y zzOeA5b@H=KEXQpfLTv1Ab7QlmtSitlNeWY)#f?UzV>c;~fwslAsGv4SFeZt+~^2*brk3!4wCK!0uu!Uev)Hfdp|9JjkX{>>+OiWMBQwv!hoB zqy8Tn%FR+%bV0fe#aQAgeS!p+>fE;w-fmvsj`pC#{fLJWLZMFJuCpFE;wcE%2mHUT znm0ytSHmp^Oyv#=KPJl4Opf+R_tud2i;}Kp%nr@8v>tYUnqna*alsv6q&XpSY}oi z_iKYiLCy0!^LK6oN~tH3Ha<7+>b*&j_k&XdNY5o{wz%Iq8dzb12hIxN7fyCaG^pk` zS)*2AQ|`w?*fcgj!$!sLVoea1=Xi}uq&GFf{LR7b2r($jW}Tp>y?^-c;mq>6v*Z)A z_^20Ys$o83MP`K>x)D8M;h>5jX&Kr!#NQc-nB- zE>Yih2X>g1^oWgx406G1^I0#GpJ1f>d~tj&51{W_EUC7%B0aHMI_%QI8a{igN@-DK7cmH-;v<=mUwN|m)uWW|L`d>f$twMy zB5m+v1J*@bBG0cCA2R$>X(;5`@voP=JirAeuN{>?-voxlwm7?rpAU4gozIHv=8lec zriP+|%ds+%VwCEH%nRy&-nW#S@mRK)Hr}anrVh(}=_KIcx(`?Gr0%cYzZ9YT(G#6n zLR`Ji)Ex;ificA@hT+V4G|My~17dzgV+0hg3Gjcorgad6g=8 zEM?z@*;p=R!8&T~J^{^h$>VQM5-Bm-pTz@rUa^Br60e7B1R(1k7$WxKPn&;hnEDej zwOYn3K8~l&C@tNkb28gyY#*?dnfEcRl8<~Vmy~?>&om=xTa}v;4iDRn8z`;mQSFVa zS(373YH8W-YwKU=UPwE?by=+5G@mhzI?3e%O|w}FNwVG_yo&>o`MKP5iBDM*Wxif- zzWoDco!(`N02{xr7geba_|e;mW_MPsAC#7B4i<@K_i4YvU*`=C z;6FoKJbECvSt8BzDW3seDYBkDJI=d#J!QpJoeU;rZ!>i?s4+FEtThOLXxJKQlxvHo zl)AUQJalm~kDhTsvhhtJ^@ieeL6f7T=0yojY@_N$JNcaAPa9PV{ViAuw$rGd1hN6? zHyZGyeZBcg>I+ijj^W$H!ReMIlUu#64x(a3nVz10M#T-ug=bFVus>7n};Vpv@SO~-}w7CWOCEQ&y{)4>R>^*(9z!_ zgCd#=&&SAU>k~@8VKtt|ctoheSejCtOw{BHpLZRO%OPEjRuOD=N|Njn`1!(Yw^dxd z_egH2KJbzAvoW!!C>uOtQR%0iMc}pgr>sa#!{DDHT>(WERNHdJ0!O>ZZxN0c4|+no z9XaB7x$J(N;PgvI$l6X#-4tYWmO`V$9&EX+04_b_s&Uw3AVSRDsO_K7`|eWhhi^h; z64-&)BwhlX)F$o&Ip&`NYNzlrbeai$baXanWK}UQKA#Oka1m^|lsu!}u>HH398u4n z<_XV6Rca~X4atC8j<05Z7SLAD@RMxslpJOf3X9lBsE5ugkNo^#duWua5ure<#+G=}X zMXEOQ@z+#gp4xY2uFx!2-L5BK&M$~yRws84DbO5=qvx4fX~BzD@c2DydU^zho&vbP zEU*S)-VbxyQ0=>6f-^znlv~wbe)`4C>Emg!Im-D@_+5DsA+D2qW#!yGl9&Vwr&m!p zB5=2layKT--g~DWp?kSGuaK%KK1+f`{O;4A&jwZon$wSsqwJ=5Ut8`R&ksfmymjQh zc!B3i-ty7wxqerA;e`YB?~!ng0#B8e91h@8zP3cQa+58WRqf91J-OnaoyY4BE`&ym z=ObjGz#cy$z(sC~x3ZumD*L9;F)touq_XFFZCQo7=eQ{~p%xao@?MaW)>0gPYWnh| z!$zwuvjFMlW^aFua?e1=NyHE_{ELw0t3S0>G0VqWsgT+9bazs(9e_X&ysj8gt{XpIwEN-k@vDIBQrtfLr~3OXh!w`_>bBOfCydQX`CcF|F@e z;-uuY!x_dcTS^fA>l|)X>l<>sBb}p{f&KiD6o6WVmxR@nU{Ap>?%WDGfY@z!kr6Vm zQfA-+^Yus6N^|nFEGoZ zJ`)Hi5M_0-1@2leW`ykPgy{Tkc+*xTi7$R}T0cY}o$)kgx-Su)?47c3(rK=O8R?VN zqSfHxGtjJHCX0H+Hm>hwg9sFl#qXq=@Smn(?|EqLVzUSLa8 zZHvaSa6QW$krip`Z{J(jVv)Fj)}h&Hl{dW|$*xs#Zj&<6rZHNi&?x_c8@*7uiI*G5 z?~j03f9mFZ$QA0HABm4s*_ZbXRrBpJH&Er2`f_hNc4*Wqj^Q>;hE1C&{!@w-GwnY! zlUJtfdu60EmjSE@+16xUQVO-AH2?XVtam;tp4)0<;~Zj~fs+A5rc$`U>bj#br5UY7 zvttg=7@tglSaq4$}WQq7)&VO2m7=?dSA{* z8rmO;8a@~pAokHj<2N&5LOO!qW=5{6v-wMv$~AIxbH0F_^)F`;)}w zS(#p6?CsrJU=Kvqk=9~p0?rn5d+GJd(dQVkZ(E0<;Mm)m_q57xM73wZX2uI`9_gm( ziLBBeufO+q#om6*agD3ZMRDH?s!y{csq?sa-jCGf`**VZx27t_bpS~9QY^vNigjS7 z3wdz<2=)z=njudzP{LpiiqLh! z42yP}n8LT(isa7q;o715X6^x;C7zUU`$UQ$=4K23x%%^K6ZrrUx$$*M2RNSbFK zZ2!@{jYow?X@a;(SNA${f)tp%tBNFz%+8)ZJ6VL1VvR5TD32=GzX9nMYz<#;P>_xu b?M#+T;1G7k>mKNklDsE<3v2u)%~?CSTrQWt>-9Q9!vX#- zmxVGm5evutBq1a;1d(I73p0*j$&7LT#_{iR9ZAaR298Ne8m}%(Qr;Q}DP5jAreiwh zcNkV%=oT=;7ENHRnJK1;D}Un#=UGFIG~kZun2za~j`^Q4>_WDJQFj8(llFwiEXxM% z{-eMvz1d*25W4!xDu5w1JVH(AH84Lks&exUb#0*+j;fub*f>K(qVJnx80K~`WDK=} zk**Od*$BqY9m<}j&dm50W7m=9pb^>e9pftbRBR(yHftZ^<@0*YqFFK}U3S+cx2Qv=^tzdW_B)JU?uOA|E z3mD$kV{bH7Ftj^)Lw{oqXOe`)&~ej8P|SJLhs5~%EF8wOywLm_qlZ9f3?^uaYJPoX zSosvium-GClT@W^pEQgefmPad$;WWQ#-I0x775b<@wXWFQ4hVFC{@zp+l2{}zsQze4Ew|Vy_Ih5afrV{SpOBAGw7MfJV@FM(YSN` z6^6+gf&JgNw2N1i=XB{$>oyDH=P5Es!o>DFuG2b(lAsfONk3| zRV7P_7+obxi5O7^Bg*WXRE;d9VswoxrD8;lEG2JmRfQ}iV|0ZqC1b?TN4!r?A#AS2 z7CaK+>?{?;$m}eYJS?)gSt^OqxmhZSq1-GLJ=3$9S$`^u(V1B)d)jHfx0vEbuQo4B zWic7!9+y9qHd$Hf--YOyj_H_=>6nh`m;xBTwhV^})Rw_8q1rMSCRAI7!h~weP?%tC z83+@uEyG|UwPg@YthNk-iPe@NFwxpF1SVQrhQLH?%OGtgR$GSY+L78a(4ZHtE&Bj7 zO$BSDgMYPUup0kRZ5a#`sx8Ca{UA_V2D}x6Ut5N}FN9ZH*6%bJy-NrTBk%W55qQk* z&N7U~tTiRZ9Pe`j!szvqOiMA6#$uNHR`FeBu^4)MVVd?66b`czCfC2I6-)!;OviLg z$8=1`U@*k%{R~+&fYGty$R&kc=WqHa=8?io(r-_I0%-7`~D+%}J#92qz00000NkvXXu0mjfV+AyG diff --git a/examples/starting-card-game/assets/card_diamonds_K.png b/examples/starting-card-game/assets/card_diamonds_K.png index d48009e01712665d6f473b9f01cc2b3b55f8788c..4f5809ad4ec99ee98e3e950bd5e2872b18afd680 100644 GIT binary patch literal 3462 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 UJm>e}-$@Igr){WJsbLrTf30a$lmGw# delta 1339 zcmV-B1;qM>8`lbu8B_xR007Xe7pedN05DKYR7JG3w7I#txw*N|fPl$AKmY&#@bK{V z_V&BGyUt$OG_nmyT||l00eYWPE!lZd+qBOroxdS7k>o3 zNkl-ZHGeEHU{M$dJfAT)gxM=;SQM%Qe_({UM=;WWF)H2X zylEIKk{n6ESdr970|xGPm?+G^n2B#e1I8MRgkWxV-zW^g{ehi}CLjXyE}2B~%saR> zkqo>#4-uIAg-XGUcV|AYxdQ#^sBCyImx@5!Il_9W2sGp)te2`URmj3{PJahR=-a!Q zD9p5^7&Z}sfj}v!-Lbn01A$iWRc+T`WT@3cf)RV;DX?K|kGGl=QYbJckapWc3Izs+ zg8?Rt8cwDVVc-k{h%iT&ahwLlXWU?N!wDy}xeLRwlb#H&wz0|qg!Un@V8+#n8HGZ* zhSag4L4&a&b!zqSV2l%=7Js&gFrzxJmM~#F>o6UrF^m8~2Zjxd42*r$Km_JpT7d-2 zR_1{K%-#S=bQq@@OVZo{BM%L{M1|pp27b^S5$42_qWL+D$C9A=HH=_`Z%q6IM)5RE zkR?j9ugugXm;q$+&O~66cSgc+3C8SUF*U_uco2riVJ5}mFTrGdhJS7Euf!Z!1z1$n zfyMgF=9&X^Ug&)PjUaLis;%y&TAV8gVw> zw8$qSc^yU+H4HC;~A1ab6T) zKIg7cfLYIXoF)=5?g{}P#(AiR596%l@L_DOQHq4m*jG>$k!XUo|D^zh;2ZC`Ki=^^8Ke*l0J_*+f^va&%8=P7?PeI%fgdhQ#;U~)!a5CA|;BImIk zGsETyxP}cd_r4zxWasAuxbEugX_r%z;lPN2GC(0Lf={jgjq$LU5b7J|Isw5KCyNbm z!uFHuenMC~p+Z%$bdP?#$MbY9GXn|!((4wdWnbB#+=|Rf@d`=SBJ4?KwK7&wm>4FE zlmdk#9~;~iUFYg6P)&j7Y)=l+=(+SE(l&T_`oZSRgA2cM9f@i`aK|h3FVp@T+I#2t zk%H(+Uay&!agt%t^|)wqQth^$y%AENQP_~Pgq4+b_?8a2d%t2k{+;YEgTNxypR~%< z1gfzCG!?mEAfB7)@#l3vI2@=X?ak#OO6>SC%Yo>y?1z^q@r?@ zDDR;{RK@g9PEJw+g4BADmLu*i66xQkCPznonfi)xQz@R3&u%pGEuc(nbESZ9au;i4 ziM45sg)J>neA08nu?j2*nwx>m8ikixFYP%lM!<7{fBCNQ%BD_|pQ<$BhV_&hS&k53 zjeg~Gk+`t#4GEezmkozEEYv4VurB|#C|P@-K8VbZEEAUvI?2C5-`GpAKNWn^XWQnG zqo0o$!@sd@hFW#imhbb5ma|g_ZJXt!j+M2gmM>XY$I~L|DYLKpXtn^9eWL>0StsND z&%EUc19jJd&W;wX(0%%j@7__g6uh+x$)&!b6XtRNqWaj;o+SB5z`V)jDhf}5 z?w@BR4M;I=2O3gZ$`E3T-9?2d)wY)J%|9pBEeTP;31E;DDR{`iL5HP%u+AV>GFBM^ z3YB8A!{%Ex?Oi?J7TO&UoO`lN+wniU9$3!Lu7hER);qWyD0ZjV#1df^R$V;D>H3?Z zrJ#0=hGPqKldB<&NU0y5rQT~%Ffqx=Pyydl6}5T#*X!X5PU_}#JLVrqOdM?4n>A1M zexUeyX}*xut+7{Q?v+4Ma48$fJrYg$01;`5O={iVjNHTq_#> zE9!#(M92Q$+dJ}fQuoCe;vB6rGjdqTe^)MjQc!&q+p=$Yz03!jE?~R}xjmjA~GvCd}|aTGY3$ zIo8CoOm6i4_6^v|VX~($e`1|5(N$PQi+^bPE2eKv6NL_rN3l-eBdhRR&1X&GAZIG? z>ND)p^RX`@FE~`(P2E~FgjJuz>b+#OtXy~Hk!o!c80wrk;?C8q^0~x*sglWjfXYD- zu4H;Fw0hmD&aN%#_YSP12TB=hYsN3kfD{ylTv0^(cZ3KKSSp#%$e$T~FY(Za{D6nR zk~2h;AW?G$-6OkST9c;H>f0U{W#O%4docTPhAf2&)N>t^SkC)BQMdb>J}@594=wceP^oI84T%~dt|{8k$0mo3b;TdyoQhK`&J%z?FH;0 zyDvXxiTTXnf`z~bqXnrfH0S*qsdC*6r=Czs6#-E{lA+laW6d$)o#ijq^T|QzvzeeA z(!a9dt;>i?;oY7A**jDdJ_IGdbWM5d$05CAEZjg&88|BuT{P-*mPhu`^gD}?{Jp~^ zkC_3Xa9i>OmY!lQ0cxDT$N|cWUUHi+e;ASMQ;lLEwqGGL~34DlS_0D2=mpGKQwa%a`k%XzStOy;;l;WO8$#ZvaMQtTDCVjooVH9T>`6cIz%Ki$^1Ybd3=)kTwJ^mT>_1=_r>l%P0CicBr*Ch@vn(9t0dEyS_cl zlw!kb$_Hx9af%$PeP%B3&jFl;%6!768r4;6ROImZ9$o^?kY=~v8nZk<-P{(}e#ube z+zGl^E#TOL^eT%gswZ$QOgN6pT)Xs6HK|hV4k_EyTc&ty>f3%fT{y1yp23IE`TO-s zikeP94^nSl=|g|=e*7lhb%IEH%CL%!g`&(&^+E~7mi_hJjV`Mt@G&!|G2;w@h=W;>?J zJLW%>JJkT|*=@H0L5~kjR|!4&L8WbZ6LT#AR>IFQu>O2CT`aHetM?l=_sqh7UufGjBHuH^%-1BVE@m$UOLPdIs*axoN*FOD2chwJ4Z-M16~rw~`bLtgTcf?QAXn}FR>e|B_I zQG}};EZ5Q&jZH;AGiG%}ta6+r8mDdZw~syk z*kwQfPKF9aQQhm=*4$Lu?QFm_PbKMiL`mlVao~S7$p0Pf|5+q}UhopNBSsrjoCR-m z6E#ARe!@C7Yb=Y8U1W%Cb`6qYO_LwFXH1KK?K=;@&Nqeei$^SHeN^pB7?&6^Vn6_7 z$Zb843o483cdHeTCjdYp<$3*tXYdPZ(^neL3*P9bm|9e|>KucrVx7!k;w9=K1$jSq zZ0`XtKgS`B3xvAbVl!ljNvR&yW|_COiHzyk?n>no>MDaFv_8VKUoqxhC_~)o7^+r# zSS?Fr+v=?0H>gxva-7v&vP!|DtVMCh6nyF89jcdnf3#bmu6*$ZQ-?-!eFs~zBhT7i#cxMH zqeICuNQVmwic?k=M59fnTY_uqWv_{E&j8=szsLk>;v4lI@4 zbKvPcZCQdtnXc4Qwg;RZPZzD!ZORb&;NDn%&ZsdEes0n}u;mlh=hcTKn6yKqlGjgp z#d03SpZ)!QVf@?(nehaeLVP;gy@ez}lQmgxC)Vx#og5_6sMd{GTA$7|EbQ5J`}Nb0 zI7T`F8=#l!IVW8M6*bG#3mI0L@x_4}hzrne?6+qbyN}WzI1U}!KM+fPgZ?&O*xc5w zW0IxfzZ35m$Tf*D7gIOi_%{6NvMCQ_aI(2-4Kb^R-lC4$|eDk z*)mH%Qj)qowN#nS6>dAvtbhCv{JcHcF_AHbl#dQ&*Np8j57)R&(P!<+e z>ineO(1(gUCETPRy|nj>1by8w?<_F^+qq%nnbcoLRa%P5r#!J$ke4RsFMxH4Xz29; svsno#Y8b@npv6=nTZx-P{OwkR&0zsAxa9By8<`7`8D9ec007Xe7pedN05(ueR7JG3wDtA%%0NK)`1tVf@X0?vySux& zxw*Nyx&QzF@!;UpXJ^e$PTPWl?#jyOw6x)yoBQ(e-I(;!00009bW%=J3;+NB|NsBX zd*rCG$B`iye+2YNL_t(|+U=d&ma8BPhFh(jHb8*)f7vZMt%9JoF#fE)%+1_%>DQdG zlX$sYE{~s|pF31~fX8JJFvuIovGjk5=n9n{pvUNKbid!O70B&=fAmJ`wbDMp()QZv z4Rya?ug?nPdhP9W!kjSw8%#@N>wsyskWJG_#dsKYeo2DdKwFbA;%u2lR zvxHF-0XrHEgmD1!WgG331cY&lG4HT)CSw>(aC;B!a2Rq{np&GyodJW>ijF$03fLhNOnW@L(RRjhNPr1wk+Idtn4~zD>-Y6uIs{b z-Db+SFsd8G%f7d4`3^>RzoPLl)V+n!FeE1!Nmv*wx|vLc5(1+-PZScybb%Nkj0#~C z5XN_znbSagW*$uQaKeN(&tVvy^z_7{@#R1*f8rVl^Za#UQA42|+L^GSfx!$RHLFWl z80W-69}a|hn(+9BgjvW5bHe<;Fr-B_|Fkc(XP?q?g=HhOUog!Za(t!|tit{GplTTEE_LW((oPZ}X`Gt9=f$ z7skqT7q=POzFt>7;7n<%yHDT%gkvi3FQyf+dFqdoz+gfC+q9aO_T=f2BrS z>HwCC^uXb5-BYu*-fH}WO#H-{2}4~0+4@(H-KPUp0LGL$s&D}qDt2xX*(|EuAx{6f zVm>)h%1jnf@_3u;!n!0(rVC3ddhpM)lPdcZ!sJ=0EKHW2Wa;NKGRsPZVN{lt3d7{& zJt_^8QMyI>XL=&XWlF=S9G58#f1`6;rZfy^WSR2Mfph$~ILx;mY|ei_IAKnh6Xt|D zVNRG6rVI>oJuvUtvI<~gvt=-t*lZaJCN^7!f{D(SfnegZWf+*mY#9V5HCqOONzIla zV3Miqoy`aa z6Kq@a_MIILqx_{}p3@u-f8)EX&xY0v7N#nnF6J58$oMjzM-3?M#X$c66raT7#Z)MGrvLx| M07*qoM6N<$f?<-a;Q#;t diff --git a/examples/starting-card-game/assets/card_hearts_02.png b/examples/starting-card-game/assets/card_hearts_02.png index 5589d8dd2ae85f69d1b22482cf3e60b4a6175bca..eec4e133d0f02ac6b341f2d6a3df7d904ef1e9fb 100644 GIT binary patch literal 2740 zcmc(hXEYmp8^%Mah}2#WrA82npg~KGs1+4ri`EFLD5CbPl^8|sR8W*y9p-};RgFDr z*KX@oEwyLO@}|%G>HYG4eeZMb`#RVC>HeSdJO6VfppEn{f%rfG0N@f*U)z+bNz`ry zUZl8YOC zPsiHN4Cmq(i1oP#&~>}-iOsJeIspJ67*ZQy9z?VL4CP^F#oe8PQeW#}X8i4F)v8pX z;9lo0rkmrITF7smi7FG7&jQsskdcZ^Qt~;nDpID+rbIKg}={mznkp(FN`JoI9L@^EFt1xVSepR^hrQT0OPmyHDqAG10=3W4tBQ@N$_&}T4yHm`LouV2rE_yd0gyk zdV0P%QztW9Ks-DX{(08EiJywWVTJnV?9Zf13>#K%6ro)9%#>&R1k@oN%wbD%yPJ70 z(#4Jk#w_gP;NSU3`a#=b;8-DT_UcZy@4XQPS!II-S*ftK?tt z7VAy2^R_$h`*Zw@UGF5G&L@!O?Cpv~&J$XI^9K-PlK`cmcXr<`@WXa3TYT zT{F-;J8N3ii+znvRSxoSiKQm44Id?)ZD%F^L9IIlRFkNQmsG72(k2>U~EB3u!^qOn9+j-og30D^8M70Uf1TUmRSL>I?~@wW#$jS!A1 z9*t&5BF}s8#FyxcBfGz}^u3kwZ3I4)RCfvZ8VghoNulE00R;zewX$|xqYV6DQeueStYIozcrF1ro8xPnrG zn>Y``)R2N2qjWH@ahS2|1(nOJ!DYY(FjQ%8`TD(N!^7=*@5z&cj7#4Ycw5uEzjCra z3S+#$#-2OAE&|$b4KSG;RhJ?uiibE;TXl?Gc>MKvsa%@p4IUA?sCTtF zmoK)Xi@Wa%p+swgq{HNjO4VBVu(icJWD8B=+!WVHBnZ33w67A@@VKPH?T;gReI{Ct zxeH(6F$Z#>#PM`7Ago}g4YD~s-JFXuZy5NpyWlVFUMP7tk1&>gp&+s`}nH5+v$(wEDQ;Njx!83CBc z;~YD%aq_ETG||F*VsC<$t=cB{hHs^pnfpNO!b$Kmg?agm6Ng{svz&~p(#F?^p#2wk zu~Ow8kMYOE>}#Gb*H+x2h5~cR9ZQ7qs9Wsv(G*ruDER*-w(wPS%XV^RUE*lN{Uy=2 z4sb(sHptRtv*%Q`%=u_yrR4ka?Ci+~(#_1r$wSmzzsdN~+T*$3-|jr91;7K(rh^*} zkHmyu%2BR6pZg;eiVG@F_Njc7KX{g)pkTzw@laC)ZMtC-;&nDP2h#P*_G`KR=5@%066R>q`70PCq@zORYw;z zlYpruo9zd4Ltd8BT>d`A`HgYOiy&3D)2t?vlt%M|Mpv3Ubk>7acLx$xiaPA4B|cXP zzIx1Uo!Nh#pV(Pdg_LYo;fRLTSjCc8Ki%?rM2^DYjFIQQzuB@V=F$5cz~P?MXnIZk z;_c-Gi6knE8TRVrnIec4hDCOc?SFzawb8CnZETtSW`)}iy9;0T1cRK|ex?leE_A{s zzAQ;pA{TldjQSs@($U93ZnrTG7+?TOw@1K|JzX@MU9_wir4`+%E*eC=ZLyp%!$`4G z?f8W>mm86F7*6qyLSi@BpHU>u9%>jbzkWIDTg#)AJ=5&e=;hG?*w@W8I}nGdWoto} z%hpPIE`fs2bX5@k$hsd4%B0{q|E|NiaPdfpo@!#Xz=Ou03}Oz{Bx1z^9G0ICIlXM# ze8;$wLc$AFkw*Gh>ITnDv=*K~zoh*YXzBNj|KNs~i`(AfeYK#04tDQU+1Bv#jY*;S zE69}(!U?Q?mvii9B4 znHXH&pBr&smY9z{sMlYQGs~z@78&XB#`M8}BLh!OiB&tDr*6uHtfS!f0L<=#0_nMl RK5D20Aa#tiE43V={|)e#=`{cV delta 1169 zcmdlYx{h;#WCAk-1H*+i;;Vp^S%6Q7>y|BBzJ2|=d-v|M{{B06?)?A%|4DoMUAuMx z*+6h9KK||g{U5Jg`}*R={l30u%a>ov&%aeu<6@}t1!w|SNswPKH&9Rf|Nk$xmAf3- z@?@f-cs&b;r;B4q#jUqH$`<@wo2_x^)vxX4KJTZt-Ck5)b=~E- z32Q`2?*Wc8_nR2EaHJlHSbl3;=v+;PW7`*7mPuT;J6n9X{)o4T&W#K8SI-z+G}M}R ztLziygk+{Iqn8vSm?}XK1!w1g!cPol# z>dmWCX#F^o`}PX=nk!~)%zrI{+XRlK9E%SK-tw+FxXO(+xyX3(yr+ETF}E(7i1vie z)cI_4ZJU{;v5Tt9pX&PE&wP)6k<_)ktYRQh@7k~fW8tQbU>&ZPIyMDg#E3@2gwym??;m$c-Qa>PYqfX+o6Nx_cWd|aDJhM41 zl*T0!_1wK@%CSjT4?H)T7kK@ZM9_@LxDPFN8?9#6@t)K>#dK`0@BZ)nY?tnl;EVner(M@ z$=UpBey+M{7B>>!UCw@aMMM0I#hnQ~W(5a#tnE})lh|%)@5=Gm-|yrv{`vzq{;cdg znz2=}{Uv)$k-KQv@EhGGnd>G zxqE1XPs;R!OB2%Xaaa30>PpR6a_0S{FMoFhzi@kKy=sAZv0MD3ht|K;@|*r^78l0WSZFf zsbr5q-Ks;jsk}8;jXM|V@bmO_pHwmDf1H)o%-y1Tq_xi?=W9()^!k3Sz@18ECoWbz z=RKh;*QnTV&?w=NwNc(y-GinK+xAWUsx|q?B=INiY6qWdr*~EU^$oqtb>QdD(9o~) Ysx4pJL<=SPfn^4Rr>mdKI;Vst0BZCiAOHXW diff --git a/examples/starting-card-game/assets/card_hearts_03.png b/examples/starting-card-game/assets/card_hearts_03.png index 89f2e7c36640af312abdab30eccf516c5dd9497c..8afabee3a602d2a362afc63918c74df0bb89af35 100644 GIT binary patch literal 2522 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 Ti+vOT000R9NkvXXu0mjf@YQ2B diff --git a/examples/starting-card-game/assets/card_hearts_04.png b/examples/starting-card-game/assets/card_hearts_04.png index c1da9debb4b121d90c41ec9c140731ac6385bec1..757c0c31899f44d7c3def956baf02c9ce8f356af 100644 GIT binary patch literal 3016 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 Eg0P>loB#j- diff --git a/examples/starting-card-game/assets/card_hearts_06.png b/examples/starting-card-game/assets/card_hearts_06.png index c212e158830c2fc003827abffb68dcb8b48aaf97..98fdf69bd3aa4fad750e81c9d8e6cae8e42b92b4 100644 GIT binary patch literal 2842 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 delta 1381 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 delta 1460 zcmV;l1xxy&7S9Wi8EFFm007Xe7pedN06$PnR7Ff_asU7S$v;2#_4T>AxwyEvxw*Nz zy1Kc!xvHwFySuy2P*C#M*V}=C(_>@ir>E9$Z}#Zu@5aXKxVYhzl$?eGEC2ui3UpFV zQvm<}|Bo+c=(`WJYhrDYAs2rIAW1|)RCwC$oojoeAP|LZNSjn{BL4s1x|)j(3TB)c z3)=@iCEc^-t&sr_C)zYk^L4#m+pKK__-dy1d1;Gim(Qm89$A!gB5QL_jWplQxot%e zt!>ZEr5%sTNA5Nqo%Yg92j-)IHXWJ@Q(-F15inW?t%mu$_{gX zWk(j9_XHk>;ZD$xEV_`PEzId8MIkW5cwl@XMbF9K|B#X}!>Yb2tT^@&7_zt6k!njl9Q!+2J#Be*Q zp3R_iP#7O6cs`i3RPcY!BXnRG9XVx8@N2u^ok8fpFtJnSwABzgFwAt)xk6y34_YpT za-$E4&_QAP*vG3kI?)In6voE>pW5g|A#_lfVO)t1AMit?Q99D4ic5ClP&(RmlTGwt zkvbBLFws7YK8rQUBB4z;7e z#9<_0s`bF?4%H=LhBZuTmg?UAZm2E=v#wXMVr76u45~}jnI1BujAaBgSfRRP1s)*N zz6G9?cAvwuo|}IRWOjr0VTsq;uamA3xi9>GXnvU`x`cQRDbTEsbsWI!83mqE@Qwp~ zapg}i7meUJ>{#h(SQRHY4m)6kg#2w3$6?PJSgD-{X0PMGzQsei$p70g6vu%bhT=G| z!%!Rt{yj>~dSHk!1jpfk-CMllZ~zS6aX4aM8|^q8vdMprbsUb_@kcri2gMI?j>Az| z2$bV+STF0XG$0|SGK0n zih#+Kg&;W&+*t^U9S3R_g4J;#XCYV}2XYpI)p4-Zg{UwUrovR13R7V!Oogd16{f;em_GvJ zTtP5hR}PF{ZO!F_nOwhxN0ANY?#@bA0?gCX4@`gjTrfJe?~@5eM|Yv}m_;_e^@D>^ zK%Y6GTWlSA63n=VBL!Z!+5%D(m;q?-CUmP^OhbV2KodTp^Xr{4kA_jem_|-JsT=tS?F)ZPq-clJiO8K5v-vYNauJiwpNZXaG8atr zCZ(BR`gz)%Zp@w+W*YF_eD3JHF#2b{9&*E|`}O3#6#)}jj#e1ehple$R2U+RD+54g zdPc^zRV5r6Ayj*9zRG!&$=B^6FD^fdyFKKksK?)o55PGWfO9#2H_bm-{@O#BqqR!_ O000021 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 delta 1604 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 O0000TbBLx!c0;7XXr@7oFm}ma(pZk| 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 diff --git a/examples/starting-card-game/assets/card_hearts_J.png b/examples/starting-card-game/assets/card_hearts_J.png index 8e4bd9a418d5ed3d633d7b64ecf5cb650af5a80f..36c290b88534b182deff4b3d55b32358645a5ef6 100644 GIT binary patch literal 3614 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 z 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 PcZ&&o)dXFlcPHxK(ky3R delta 1356 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 Q@C+{iW8;LXLwcwD4_&-DyZ`_I delta 1163 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 delta 1306 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 diff --git a/examples/starting-card-game/assets/card_spades_05.png b/examples/starting-card-game/assets/card_spades_05.png index 3774ef1fa7825367c4b8029a2e09726ed11b4b4e..b9f5f495fa34812cf431738abda79aa4b7e4eb6d 100644 GIT binary patch literal 3215 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 delta 1413 zcmV;01$z3A8K4W08Dj$g007Xe7pedN069=hR7I_=tsER2|NsB>_4T>AxwyEvxVX4X zX>qx^xvHwFySuwDFE8BO+;?|(Nl8hPl9I>A$75q-!otGS1f9wb( zKPvoDB1RFbi^ItKY+8nE0^f4!hmB!|@@ zVKnUE_-zL>Nv)Ud_$_9ab~_J|IuwkK543V_+1%;d%gb+(eF0LJ;V7f)rbNt)99=RC z@!mF{f69Tc0(&^5Zf6shoCP;mh;VyIT>=J}2ONwkcVjo75#!-u6zsk&(#_klEJW^N zRWHFP^B1dnsn^>IYRo+qVN}};xms2%vkZ^>-TYNFp4rZ#b}&9~QdWhrnUiwd;u$al zX21-X0W)9*%zzm%17^Ssn6HED9;(9R);-jOf61cs_fP?bUiWZ3OhMhl;V>n24~N5))IA&yQ&RVE zI7~^Y!QX}{se3parljuS_;tn{YQKdr1$7S<+yTArp-u=vtb3@sZ<53rR0Afv?xE(s ze@W8a*o;8lYXfyIkxnNWF!rtqq~nSEe++prOUl`;=|sQ#fL=_e%0&`Bg^1+b?oKH9ZLp;bkX3Ab-@uIl3-#?izXDw0~-3LI)pp zM%t<I%4=;^(Y}cxf9njQx^)PRP|Cj5iqWzVRu}3R80NIf&sfNVHcJ*= zLw3gK@Ek`EFfYg4_5$Nd9OY5jMQa^!%xxo>a6Q(;>fevKZ3Y9KDANH9?+8Y=PD;D$ zOB;7p2FzcAIrsC09p}Ej%H+a?Q|(byI70dozWzpZ(!$r@%=6`^2b7=77d-z1mMjrC TJFCqR00000NkvXXu0mjfJUFF^ diff --git a/examples/starting-card-game/assets/card_spades_06.png b/examples/starting-card-game/assets/card_spades_06.png index 2d1c201afa4378120530b65ad0ecd519c1f0fa4e..bfa67f362becf6159afa1b9dac13b98ec6c8dbf8 100644 GIT binary patch literal 3144 zcmb7Gc|4Tg8lN=C8k5T?Wl5T38T(R*j4kWf31e-DWJwzP5+fvJU$W$9mr1t95(Z`8 z#yZ)T$QouO%YCc+$Nl5}bN@K+`8?m}J*)wmbAI4vn~h7h2iCV~swx}#p4rIK)8-nx+{r!CYBrIykvVHr zO3g3G&A2k@_DD=Yy2G?;v)A{_ppe`L+*YsQ zorx$0KKq+?w`On;IFqerdmBP1e*Q|3!Ja;WL&xc-Ge^^Y&6LXp=LU0x{gEOmqECE$ zUOtO#Hj&zj%QT-fnle?|JV|ZF?5`c2^v}Gvux`xjD{X#i{jI@sX9!=iaP*8gJzep6 z!eM3UUUg$*LE!l0r2e}RzvHvVmk#{(;5P40z7H*Rn~GQmnzBN+{dfKL?Yz9~SBQPV zonFlh&&cZ&Sx<+YUp(iUO(=}&>tisLmzP&0)QhHiZLD-4i$Cza<>U~xG1+k2Xl})c zrlyxzeQ$WI%sRxlSf$m-2^K58rVhDsqz_kRppQU%=yJYoKvr;5$onvXp-o$D_`T@p zwZXQy#;w7qJdR58tVsT?NPyfi>4uQUHowpn2*gM zurr1ug%o$^92pGI}P zjNJ$*{IIT+hrOiA&WMStWD$Q#N%(5nR%@(hV{`1kk?{mU;(3okt{y+rv=>d#EnxGA z`xd$bX3fsD^hK6&r*z+(7H`oy=IRC$nCxN`A|fM?Z6!Zp(H_o&?|zmwWD(BRHf%k9 zy~vCMhutqgmSY;bGotV=8ZgjR0o}!V#CjCo)kk}uyjN6EP;h8}PNHecFVTlB+7D`8 z9w<@Ty`NtNVkpA0voQ{~)}dY1}v4x8III>GThvNM8vWt&xEVw2YDC(IF~ zwp0{PvWf=$e{Z zO44nXdV-+4td`IjhW6|G!Z20oc{&_+01XpH29>eoj_eUzZ)P8p3?5SCtmhV5>WGt!911G zJh$o7)BW!^tC3p9Eg;M0t-Ind518k4yIc$>J#S%JX7&Vy(B(!w1yuL z^LR(EZa!)K%zsNh9UZ2D9b_A2py|hCbu~GmM&&58AzJiwV(G0eEjg^olK2CYS<_6y zXH{S|&H~{mL?F1d?2*Yg z68Q}bh5mS4;AEijojU9fA3ql6{Jn{-^EnBn7NY5tQq+L3> za*^@7R2sh7P?MLpqpo3`WF#RW5fofrVR4M9CJ(MThadi`&wEHBh7J=wIY%ZZ=h%SB)isFcjQd1OBE zA{-jW;AVOuqNX(Y&J=Mtq^6WkdCK@+&c@oR%9L@~fDaLZ#=D&VU?l}|GZhZ4DfLmF zBBlZ`9{_WIu!^Po3zGqO{8VSM!s4u(=|)&hDNW8s2?BVJ8I8wu*pEC10s{dVa)H3W zM&R5(-hJsk)>mm5U5nSwV8L9xT=l9-N` z70oF$Km_opdx!h-7&i+E?W)k;+i|LW^9>2m;tLY`$t?Ew39cQTPGplN_XDo6!KJpE zJ-DIcQAr}kCO#ahV_S`yE%tm}aN{C4;&Z2%vF>~T0=UId#8C4a?_h~#2=6sTMt~KI z+7lHtFZI+;GpOt&zofml+R#M_TK{E}OvQ}80OuEn*UrDGQA!=gTU=aE*AvArc3Jc{ zgOtkpUIsw%84>2Au+uHNzf%69PDmSrjbetRRXWlrmC1%UD*H&u* zvcmV>cHp?Si*tabtql|Roc|S|LIiewsfR4yJJP=y@r1SUA&3d`TG{Tctvv_ug_jBJ zA+$NH6*F~n*E4E7kz#qb^E`GSbiQ@-q9XIuXZf{#vs7a&r&}HCMp}b@BD&TeXI+}$ zqN+ELwYk_I@K$?4^}JWIvWH2r5i+T(_T<-frELMK!XM90#juTfi9b6}D3xlC^8pEL zF+eNr7L++b%{*w!Mr}hsO2doP)HX*LdJS@3PR_@tYSz}typLDd{-@Tdn$RMCw@O$j z(NMw#wf!w6Nt%xw>$=F~yMioj^*UET3@j{YFBD`3I$UTdD?MZ!c6Yh%S^-;(ex-PJ zeW__*8rd_Wt{VbuS}7PvGB{5V&+cv~zn!bJkPW#&Ag#N0Z=uA`OY|zxEZdH^$%0f9 zGg8qQrg~h)44 zTIspJ8QUf8#_1fa;37SOwW=+YHrLiBSHK~hGoXVNV>`uU7?f6{ko+QVAo|n1RF0Ut zW>xTuz3`H)foPte&y^*mtEHBuJ!ERcB0a*_%|^MAXZOS>eo>hHX_pZmxhO7PxMAi^ zw)Ev28WFTKG{*N~(8yr)cBC*2l(CDOU}^~R!d`g#Ru0Pf(-J-Ng1RyKMg}W=hjOS) z5ja&c`X3Y*|B?Ka;jI)0<^||U**lr|$dX&A!(*WK_TVOrW{WxmC>O8A2z3=6UwTgN z#T~d!L*Tv^>rhNqil}jh%P;!QCX~-ZyiF6(2f`&6Xx;bmHJ05A;~YKT$KG*otv8Tz z&>C0!6?9zP-Q4zUHr{mBi$dK6f7Xw!G*J9^f5o02Qh~&~s(K&%XICFF*Y$E!WP}U7_0D delta 1440 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 delta 1488 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 diff --git a/examples/starting-card-game/assets/card_spades_09.png b/examples/starting-card-game/assets/card_spades_09.png index 9e9413be98016aad46135ef6d3a970fefe768e45..e88b26eb1d930d5c4c3d4aeae34005a19ae6fd33 100644 GIT binary patch literal 3380 zcmaJ^c{tQv*dA-fP?IHkMV9Q_BuTa~!y412L`a0p0zIs1mQesbf63(FJPqeHAQNJ%KI;@01BgS72HZ(SV zd6^yMzG3G{L3xIt^R^#{=j1qR(IP}qtc>iuORn8jT|1=&-jhB$Qzr=Q)g znvdI3a-6~2GmYa7yDP`8iZ8agyi0dmdT7GmX#0ZSereYyxea6+WEmAo zp4&Xlqi(>)Mr;iI-b;|D*?_;}j?Qcro7=G5>x@FD<+r>$7E!ar7Ngcax7Pb=>q$cg zo?bA(@{T5(fephYcsW>Eqa)`ThC2HDpSyc_mMn>gh$tv4Ub^(C<6LjKc!g421SZY}iSvmoZz)Sn-CAgxI{$x&l{NSTGb z*2d}dIj>a3DloIwLocDk8BxMrPW@u@k{gAAD7ljRm~Sx&pXI~%q=oYNj}H%KowvvH z!t`GW=XzPdkdUY?$18FZyyw=i)WoXEsx)V8b@eUlUp0bKvz8qew@H^}J~QEpe``OJ zrO(3nR@Mb6MzuUXMoXtu0X|k6rpkplCbI_%tunH5PrpI5 zlcOXvg5D_4Rx$kM-K{f;LW6ieV;=W-xI0ylM=bCl0B05I_w>z|xVsuNeL++vzHc0= z;LzaU5*LoTGYHPWHxV0Mv)9*FJAO@P4J5F~h=0WozFlfD^j-NrH9r@?-h z-kI*nS9vwXk__O)DlminQisQ8YsILtp8 zkx0vl^}J0QVtm1blj^e2)LVNxCRS;^5-HLC1(ja(%!jJWiP(jeOdQ$UU0Ct6aixqG z`KaaGEMB0^kyU4Y82UQDW((ZdI?xX-1Oxe{!58RT8H7BW8nokWgRl-A?mx1oG43QqUa;htc%bBH*?*oP0?gv0IHRbL6Bt<90{-?f(V zcRlOt b4>Y=ZuMPA0q_<8+~liL$}Kn7USTX?P`d!VAX^ z+!_Yyizp;%^92Hn70P{u(UbU(DE<$1;#HEmONd;EqEX20$B@o}eb5y~H{ss)mqFVL&!{ z3)T}*=S}MJ`mSq9S61Jw8>{rcAjg6ekbNnC^1oP*U8+D}K$}2dG$v6MjY(`=2V~jU zMr@BjAfDE|-{qe?K71OOGj@0AhUdzkvB)~4XagZ8N_{<;<&rj&uPqEm^$2ANnWiw9 z%4BlpCIuu_X;;-5!#0&VE4ZP3y`S9Tilv{T2Ow`!xX>*K#^W{rU>g_|T_}mC!Dxy! zU5fqyt601hwv|t`*g{1{U6Qvnr2{piTjuv}I0bvJnuCv-&4{w<5^M-lo>XyFYiy)k ze=uK_5&3$F5l7_W@7bE5U)Bj8Zr7i2&#_;bb>p-4S<~DhN~5M4R#4R5piCTrOb(oh zu{unE$G*O0BdUg$8*e;78?IUW$S2W^W!r@Kb8X5#UNvHw>@NM7$}quU_2|$F?Cjyu zx224es+@i4>|y3rs{(R03Kx)zs3p|OU zDbza6r^J;n^#V8X0`aMXVTsoJ`1g3etSL`GhXk9%OdsYsQTyJkyn`s|c3!6np1y1U zIVYQNHq(pxzQ~Hd{Z%Vvp)5+R%-p#PA~v)FD!6F9LEbFNAJg+%d@W&L){DPcv0)H} zjE)%%DXgAc=H~NvT$3rT*Og$|!2)Dn(9~RR<2mPCok76hyr@(3uxgWKY(fJmxL~(v zlN2U)Y;vlnB=|YyRltZ<3wDT6ZsIQegQ@atX4U~v`CQ5k%gf1zD?nwEOktU}wy!xk z3xrNWq?syzAJ#8-Adbo)N*S#;sdc2f#eMqQ=A~S{A6}jvikl@Gi~n!nm1}`UKe&lk z2ZIIyPQ!Rijo#J}SJ6bG&9YiOYKIudJlQeaek$K*AWC$;d-5IsR`T23WRgv=>DEF@ zYx2S0*<&sJZvcmq!?(^_dVB9>8vkm`3O%9(j0aG4?Uc#33G9@D#l_$k)~a4jG@64Y zy&jMIrzcld*jZRj(4W>L+4wIarFT66HuLtDy7=Y`nEGRWxPtr2NoC`F!RCLcZVXN> zk4N7O@02zHhhl@OEXAU91uAq_c4A#t<*KM4#iYxRHn#KzJM8;Z*zHOzh?HZ?LM@$5>%(Ej*@^XKuC6}=#(2`gCY7_gjZOpihjrl(@P74MQSau~1b zrp|V6;PYz9M|tM-^;+qWIC`cOScaKKUb%KHu6h(KlCH99DZLztML}tD51u&ta#kC2 z>0@DQb|9zpNYI9_RKHR&HL&BFe>m1g4jov`)^gJu zzY|48K`qlAJgnz7Xqra`qw6y#hqIjGV!!u2su288-7RE$by>k) zn(C}T;K?4?`_nm-ICXrm@i-MCOo7Z>-<_LPJx)%`7JJ2hY;|WV=32Ig{_o$riw{g2 r#q!hk4DW9)%=J+j7L8fr+K%ZqlzpN+)8ZckpBfNK#}HAjjlup8k$Pxa delta 1634 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 delta 1610 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 delta 1095 zcmV-N1i1V47Uc+#8D;|j007Xe7pedN06b7kR7JM7w!6E#92^`S9UZv1xc~qE_4W0t zs;arUxw*NyF)=aD&dx?gMsaa*;^N|BVq&JIrc+Z>kdTmne}AW~0n-2g019+cPE+&$ z|Nk%l{{*&g|LMfqks%j<1Gz~=K~#9!?cG~#0g`59cg`h>;QY=QPj_Vi zk92o|$Xk<1rSrRB-^_L zhnaoGIM!Y)P#E8T_Mx?3!C_R16;m%3TB;D{D;POTG#KfHkytQZi2jA_VN@Dv0L@fEX>dq2Vxohw*S2u@8v}W5FVPnWAq-&%Lm0vk zhA@O7%KoiS3{;2bS+23%!X6;xHlvhKrNn{V}_({l^}^?2ZZIG(vZb zImejp81W*1{%?#rx2W!zc#etvpEKst_WLGApJ={0|9o>ekfF946;sM$C>Aq-&%Lm0w*@l2EBxHCngv80I~Y!kq>86g&b1ehgf$VVlj&)Z$ieXiFkuS~}`ZwU+>Br-3j{X>F&e|`;b7rGiV4uKNog6xY3zSL4`t&}>2;q}6RVQLi__o!!> z>R5q)Vdh*n(A6mnZ!cgb)yc<7njopY#7?KKEY_-rX^Q|$}_qt`x^^A*9zCQ~b(#cgF!5X_n<(~Jagm|FL8 zmnEPnM5#|2*SeF?m{Ol~mnEomQ_fQ}V8YH;^w#2d8U?1#u{@4Ba4ms}WfA9|r&X|+ z^5F+ssu1S4VHlh6e6VA}^(m9|kr!>@DOTr?yx{u$%{Wd7IBXin@efJ}_^EYoMxy`# N002ovPDHLkV1jo<3pW4& diff --git a/examples/starting-card-game/assets/card_spades_J.png b/examples/starting-card-game/assets/card_spades_J.png index 37378fd4387131f05c554aadad883fbed01cfee4..d7c85215cb6765b2e498622e9c00d3581f5fc977 100644 GIT binary patch literal 3694 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 delta 1227 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 delta 1434 zcmV;L1!elz8?_6N8EgXp007Xe7pedN076hqR7JG3v=9&wySuyf_4T>Ax&QzF@bK`s zxVX8wxw*Ny92^`S9Ub`i_%JXq$H&Lt-`{F#YE)EIJv}{@m6fWhs(^rieD}Pz0000A zbW%=J3;+NBfB*mRwr5Q0Q+|;l7k>o`X3+#JilX>=JRSrzTEI^c;!aS+y|Zv3#0@lB0QZ_dgwq>zJa0G3 z$)C1i+E#g1z|m?%+cbpSZWV&K-3Ylb7v{oz17ma_qT<6P6sGB;#SE8F7=IN$pQ#qp zU1}xZYQ{GR3s=@#3}^wrF@m*MvciFpK1TuvCM4-zp}>SBxe5oyCrMQpFb(tV!Z;30 zh?A4Fj%h3oj8f|Kj=j~JBoa(FWm+Vd$|s5;8G*av!7?|UpQts=>4}OLS1hckWNUD$ zTW2{0%g#!jJrYM4)`!RRmw)cMRk<*GFjP`B7^-Sbv0zkP*R!eMnHYyLvaaVEnj8l) zs_sG|z{vU)5*#yF#u^yR*fP?9U`#y?1Bl`EJUARiMHmH!p>-T!7=Z~>>B!>XFoMl2 zulW+BYl~3AO4Ukw*ZWx~&oC`8GzdG7cubf^mEuBXs#Gv{=V*cL-A|yV^1fnAMiCsyu|@zR?|B#_*3A zzKo&HCzcQNo3YEanOnHgc%+YxGt-Gj^36njN)n%f%~6b)_gb@&s5nVx z-v7nn5jYx2m=P<9nkPw^sKWK}>A;km6)J{$XvAiHFK1~RBY(Cr)V}}7Zg;ljEbW*m z^^U%)7w3T(qf=&WvjeldASb`b(hdyWfpLoyL5!hE>lw9;dHof&jZtZfE4z((b*E+< zW79BEp5!m?o8&LDv}@TJ<;gCtgXimC`BS=xuOWv;O47}{jAG&I9aoXmugIqoR$nK1V( zx3_un)a1F>mS<#XFXnzmmUcgdXimt|Zj3x3OS>`m6SB1bVG&EVuzXLh-1oDzAH$O| zJmWLH)I71A4;WMXlgx=p%@fc1=#?dADJw=MW+^L%Cx2!s?-O#CmZiKHlYlX~F(LsY zvVW1G4_kEpZ&gfEmagAIT$l@UVJ^&txiA;zU&7GvyZQNsQK*)oY}o4opC%LSECa-d z*$=USFxJxEqw0GJP(VJsLX-aqx;&h?*TK5U>Oa0TpUltu&g zCOX?eHGi5D0LGcAh7s5iSm<~!BHVN^+FeGDO$X0szCF_KwhX zhJg7+(6z z+9ATgEjugTzx2VU=@r+l%7sb9P#hTAz9Zw|U)X`6VFY;;KNwzRatp6A`RQKr0{>!_ o?jt2HxL$u#6b}TP`=cm|zxg-@x2LSVc>n+a07*qoM6N<$f+r`cS^xk5 diff --git a/examples/starting-card-game/assets/card_spades_Q.png b/examples/starting-card-game/assets/card_spades_Q.png index 6e67e3305bc105b683bc4af50be9643fa2de2cd9..aebd3a6baa7f96e2a47cb78de0f97e478fa8c1e8 100644 GIT binary patch literal 3597 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 X(qR6~=d;=;zcqlVp#`$c08Rcs%wDHn delta 1444 zcmV;V1zY-!9KQ>Y8D;|j007Xe7pedN06b7kR7I(&sSpqlySuyf_4R3KY4Gsy|NsBA zw6waqy1BWz92^`S9Ub`i_}kmtOiWD1#>O%-GI)4+Vq#*Hl$2q3irfGI019+cPE-H? z|Nqbb{|noNXhCLYks%j<1rtd`K~#9!?VangqA(1ALzy0xnx>ccf7hjO)E2oY?UK$e z`K2=oAI+Vi0WO!z?RLEiR=Hkpw;lv87iN$rP=FDFP|B87Bq9lMftw&^l3yMYH@FIH z+xri%+<)2@#TA5T+n)-gZG|{tPMCiV=8h!mfVqP6#ao2VomK&mv>L`mF<@=-#dB&%LrRTNiR zGMAm=Rdz*2wb6Zl)^>qQBX0yoBJ9=Gdr|nn zo+wAJO8?CW#p{HZSqa zFsLFJ#S8;YEWit66Bv8KEWmhP7?Hpbvp2;o2KivfHE*IEvcNzyFTempY5_*!@i3-Y z5`8!fG|Pg2rD6KklAe!V&A_-77{3gY9QH#PbdQj_1fy4A>=Ml6uph!ajdQ$ph2eo+ z+zB)Mc{sbA9QMoaoG^vZO6D{G6Q zY7&_xgj>lv-x{#Zt!JcKuHy8TYtyGn`n0%$#ig0AuV^t4i2{|LUI?&az6e%NBS-h4 zCw&2bGYQ0o=FXI8YSbl6V6jLIJUy-HZ6asIC_%r_mS%4E?c+gqCn<4eM1ny|v6jlan=-?Dch^7I5{VJI*97QR1Y zvb0nfCMPBM(lC${@k+yFq;6CGo}S4unbI(SF2`g_!}uJNDGfszQKtNT;N2PfZ@}!W zuTM33I}nF#i~g9G(p~SjWmp^V;jduO|Ptvy2f2Cl6vX!9cJVZB1u& zuPYNhhHyNL?G|=fFu=@gh5_MsJdn9aK?HV2Cp*jS;+m z5jfe;McnBiq7nPj!TnxtkMxrUYZ%n_O9ORGjT>luEhFrpdmk>w00WOof`7PQYx`%H zE15J%VeEy0O2!`gVM2-RXQ`s(w;8n0p()1m$C-Gf3~9IeT?v%_J3AQ@LBgU*l*CLMP+=!t59adE6U9LA-QE-_~o zm}*vL#%2Ux)=s=>7!%6pP3e$F;*B$dVqku(;_)y;KEi0sE^%h<;6QpXS}>_KGr-_D z8qYaBon**ST%lI;L#tVuIO%^2PYqRclmfg5I>*JIFlAt59Wb(o8D7{?!@`6!7j{(j yDwF<&Gd0lkEZ6Y*8&Qc2@8Y@KYCs7u2Kocm_$n!UaJO>+0000``W z$lZxy-8q?;Kn_c~qpu?a!^VE@KZ&eBK3|DzL`iUdT1k0gQ7VI5W_oVoyp7Y6figcl zT^vIy7~kI6D9Y$4!g|48%RxzDfsv_(<5CuFg$St)+0OzFpLti9Z}fZdcHTYHcU9Xj z>v&zZ+0MoF?&A7m5~n}u?vMSaw0!@)!v%e(+P)W@Iv-m1xCUr40wl!cegAjKcHZ$7 zYf`4Qt@^X==gKRGKmWazdu{a!jpuQnHvgadN&2b&sfnNXU2A@lJ`mkqwgfboFyt=akR{00FX$VgLXD delta 181 zcmey%bc}I=WIZzj1H+dG6Mq6Jo&cW^SD(znXAd7jka%6WEl`}fB*-tAq2WN(#CfVf zE@y#9WHAGSo-znCRxGtI0}2Lux;TbZ+)7q>*8F_g}a_-yc4o&-r|Q{G8AEWO;d@RFsY=0RW)l>f-Dp z)2e@=s37AUex#{P_TyYG#sPq``o91HGDTY!f^j}5IKbfPev%ovNJn=^0C<+Kyb~rb z^9SM)f$_fBh{S4HJrWaE@vI~q0OXmj&W?Tw;IiNx);B0(g6AiNsb-6=W%w>ANTIQOi4J`kWjl#f`<& zxV3w%ONnP)3#}_K9CUu7Qa#Kv7|)31{_<3(9({vkfZ1Je%4LW67(3`q)u^r-b~zcX z!e0hOAnAux9#?iIoXn5d1-u`4}|&a){!P&Ut6O-Fj%=@XRD59On!$KLlI{6O)a7BmtkvvnKr3K zbPpg8&JW$RJ@!Ji874s#GhgO(bF@A{8gfZ`#+3a$qojo(2!7pm?fRMq`;1ZsGNgUSBBda%R&dXIlj0shQf(ByS=WKQ zQM8?T*>CK=94~26554gYWH3y026Av%Yqq$b(Iz)9l%UbU=2cqqoA+$%5dK|#eXU^o zHH|1|ft!3;!*_@LH_0c_#t!eL@YI464lW0e*C?v_$SE5AkbVe+X9GVX{*V82Cw?^G z>F5)%{XR7|4o&kezEKIbFG%Q}BQ4RXJo3df1qB6_h2DVy+|+RSm?vQ8BZYD5mtsa=Z4S< zNhY+mMClY~d9oHtAe9r=Hl}|MH*})hg22k!ra!hZ-&z#Y&(BrZld{zGc&yDu&Nq4_ z^iF9sB4J~uPy|hxm0gTi$go+P{S|H!hUfO!)7^8M<%}I(zoI;g9Wv~jia}dY5w~)L86#0cov&-`xs{wHGk56}U$VIu nQ~1a;%9N?jB0T~Dx9xqkALDSP(B~A{y#uZY4`&8E^tXQimD|0$ literal 339 zcmeAS@N?(olHy`uVBq!ia0vp^3qY8I8A$FCoGuTfgaUj*TzxVNJ>1K~8qID3H%t;1OBOz@VoL!i*J5?aP3I*F9YvLn>~)y}MD6Sy6!5 zp|AO0blInG&a7b@RwcAQ49e_L-<*AXNmZZq-p7~N^RMyw{EaJoci~LI{qTsL2e|h3 z{|+iEStq|EsOayZbD!rPsq;@+>yp^(b6=Z<7T{0u^}M>Pc{PzABQ4}^e7E_`z4^2D t@yFh`KWD#x+|K-Ix%j8!+LNB!+g1KrP#k%zP!Z@w22WQ%mvv4FO#sa1Wdr~K diff --git a/examples/starting-clicker/assets/StartingClickerBall.png b/examples/starting-clicker/assets/StartingClickerBall.png index be63d6b40f8f08687ebd8515a1781f05651f055c..a51bad8ad59f6cced4e95e4e01693bff67d98323 100644 GIT binary patch literal 14347 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 diff --git a/examples/starting-clicker/assets/StartingProgressBorder.png b/examples/starting-clicker/assets/StartingProgressBorder.png index 13adedaae6371e4ab96f7e1d1229cf4522b71118..a13357fc507bf6c42d28507a0a7fd4e91e721080 100644 GIT binary patch delta 245 zcmX@c*v2$LvYw5BfkE2p)OH}nmgMd3!tfsi7wla=87RV8;1OBOz`!jG!i)^F=12eq z*-JcqUD;o;i!%yyWC=Gt1qw-)xJHyX=jZ08=9Mrw7o{eaq^2m8XO?6rxO@5rgg5eu z0~O_Yx;TbJ9DaLaBWHtwgv&$gLO#cWJR$Gs=RUJ9d9oaI-E#5P1f~5)fA4ZwRUMjS z`z&>DOWL+MI-Y5NZg8aiiKsof^z)6#Rfi1x=9gQgb-6My2{5oIFmO09{Q0UF_Gy8G o?7t?aT+5s98#&${pT0!8fJHR^*f)jgK&u%%UHx3vIVCg!0OlB22mk;8 delta 181 zcmZo;I>tCbvYwfNfkEeqRV$DZ4e$wZ^~o&saCe6zeO)~udwz2+A5Z~nNswPK!|(k8 zllwMroB$NyEbxddW?FVdQ&MBb@090c<0{{R3 diff --git a/examples/starting-clicker/starting-clicker.json b/examples/starting-clicker/starting-clicker.json index e63d3bfb8..2a99b9be3 100644 --- a/examples/starting-clicker/starting-clicker.json +++ b/examples/starting-clicker/starting-clicker.json @@ -1,7 +1,7 @@ { "firstLayout": "", "gdVersion": { - "build": 272, + "build": 274, "major": 5, "minor": 6, "revision": 0 @@ -209,7 +209,7 @@ "gridColor": 10401023, "gridAlpha": 0.8, "snap": false, - "zoomFactor": 0.5468750000000081, + "zoomFactor": 0.4057518905943888, "windowMask": false, "selectedLayer": "", "gameEditorMode": "instances-editor" @@ -236,13 +236,14 @@ }, { "angle": 0, - "customSize": false, - "height": 0, + "customSize": true, + "depth": 0, + "height": 232, "keepRatio": true, "layer": "", "name": "MainClicker", "persistentUuid": "fe58e2ea-6a73-45fb-8991-768b151dd9ea", - "width": 0, + "width": 232, "x": 640, "y": 360, "zOrder": -9, @@ -261,7 +262,7 @@ "persistentUuid": "2ca8b87d-77c2-4daa-a91f-568e325be5d7", "width": 224, "x": 64, - "y": 646, + "y": 641, "zOrder": 2, "numberProperties": [], "stringProperties": [], @@ -316,6 +317,7 @@ "adaptCollisionMaskAutomatically": true, "assetStoreId": "", "name": "MainClicker", + "persistentUuid": "99f090f2-59c0-4147-9e9e-a505bbcf1778", "type": "Sprite", "updateIfNotVisible": false, "variables": [], @@ -366,8 +368,8 @@ "points": [], "originPoint": { "name": "origine", - "x": 128, - "y": 128 + "x": 116, + "y": 116 }, "centerPoint": { "automatic": true, @@ -800,6 +802,10 @@ "name": "Scale", "type": "ScalableCapability::ScalableBehavior" }, + { + "name": "Text", + "type": "TextContainerCapability::TextContainerBehavior" + }, { "name": "Tween", "type": "Tween::TweenBehavior" diff --git a/examples/starting-draggable-tiles/assets/BoardTilemap.png b/examples/starting-draggable-tiles/assets/BoardTilemap.png index 9b58fa281728a552713423136efa8cb8260dd879..7ccaa539efceb85bc197f43a814f7d30e32aca97 100644 GIT binary patch literal 405 zcmeAS@N?(olHy`uVBq!ia0vp^4nTZ>gAGV-xtR*0I14-?iy0WWg+Z8+Vb&Z8pde#$ zkh>GZx^prwfgF}}M_)$E)e-c@Ne7+Lbh?3y^w370~qErUA%=FyEc^juC19b^_ zx;TbZFuuLDG4QYhgUiLK+6+SM0uHHH95_R=*wr>L-{y^QlUl%d+Yb%8e6-$>-BFhKdzH;>#pzo>-QgL{hs~P;O_bO&08Km&vgj9_uUSn zm*K^OT8E#%8}>77HPcjgIK?@kh-t|j2I?_cp!UG12lIckl__^QDz9_O0*QIL`njxg HN@xNAEx?dO literal 201 zcmeAS@N?(olHy`uVBq!ia0vp^4nTZ>gBeIRvrjbvQak}ZA+A1|h0h*7gdp*{a$BG{ zb4ie2Fhj$EsEPAbfn3f4kH}&M20djEW~^9hUj`Hm@pN$vskrs_+D2XmLk^aMn_upK zuKz%l(R1j+}H8Gi-<0063Kaozv`010qNS#tmY3ljhU3ljkVnw%H_0004VQb$4n zuFf3k00004XF*Lt006O%3;baP0000EbVXQnQ*UN;cVTj607hwNZDF3VmSX?_14T(h zK~#7F?V7<(6G0fq|KDt@ARUgyLc&6bYVr)xno>1kN6Y>>jcp8CYw-q!vz zWJDn&X7Ddh4u3>5s{P(?*XP%?K3%65@{1@m`aj3UsrpQtt$Jk>8Oba_+*#OwFozK# zkdKVy5a4`JfG{7QEN~Q_F8}sb2P=1?q4j_NqO}i?7dYwJ!Osw-3PTd{2q=|xVtM&0 zmym+#8ODXF>5*pAbcHRK@M~Z^NwRsiG?haRIu_&gIWl z>3mP(T&x0lQXr2HXyS~ZjDti*vO7V|?{OyJriW+;8Oba_0bv6ps^@q9TY2aMU?_5zwIs(Rn~rfPXgN!!l9ZdjLU11t1jcetm-m`4B`l z1)weF{}Iy5tZ)R756eh<0a*bodVqhwA?yWY1z_@F85W*sg?#|~uun6(jA@PL=hxnp3A^fnw0 hhr{7;I2?{#`~}Og;AGmMfQ>=a~@U+_?=dY0X<@}isNspVYtEXzMk ze57Np+WXh^gVd70IepF!lh#gKw<|M9>}8_pNiOSimjnb-VnbH5Je}$tVDk9gn~%IH z(RsI2hW?YuSC%uZz7@L8@nS+P*M>EB*KcEA%UyyDCbU2xwp z^|H3ytr(uBWfPfY?sf0sk<6dsl|NguDxpvoaD5J3Q`-eOMJ?^F*&|&y=5D z3a$@Q7pzw<4s5Z_RoWlNHf`zz-3FfS3la+s8lCyW&20Xe{R5lBrYVLG&rkMpu?yN0 lFmb!4)uJCiH`eUm$H4f=+Ti$E{t8fJdb;|#taD0e0sxYK&+-5O diff --git a/examples/starting-draggable-tiles/assets/StartingTree.png b/examples/starting-draggable-tiles/assets/StartingTree.png index 5c07c90eb5187feb96196281db00b2a08ca9dee5..dad42009f6064b3020f6dfe70cd1de0a76fdaef8 100644 GIT binary patch delta 1217 zcmV;y1U~!Z1Ih`I8Gi-<0063Kaozv`010qNS#tmY3ljhU3ljkVnw%H_0004VQb$4n zuFf3k00004XF*Lt006O%3;baP0000EbVXQnQ*UN;cVTj607hwNZDF3VmSX?_1Vu?i zK~#7F?VH_B6G0Tm&&<-Y-If&b>q@O}pm%CCS?&aH${XMlfPZ(OBw&IcciQ_6YDAZ3ZloD9|dv#G6|-e;f5UY)`J-%Y$aL z3Sjimg-*vQapo{Vf=09n@VIbi^eFLple3c5anOjCBv?;;bgc^k@QO-1yNT=bBgGPE zKuZJ|#26EAK4oPg+kY7>{@wyFi|JHNTS8PG0d8f_Fn@Yr;KJ3T?b7BCs8#<#>xJnl zi^?Owt?XIpb1mCxVH?p^7L^JxxpEuJ+Gz7W9DOs@*K%GWnc^=~{J1?;xNC!AlnOuz znG^R}E~AZjL_x>0DFbB!xRpJ((Qn`p1vwr?O3R`W0Wk6L?AkVmlfCJVpEJ#NaVVwJ zD$S7+0e|X=kK{lGyez6TN7Bon*J`f;<^Y@uB8)ju-rfXsSTm%Rvn*)9Kp?X6$+4S!68sVaJc5s-}3Du4|S?%`*e_gR@> z6$}?k%~3-b$*X9XNE6^7`wI;Z9DTz`Ku}V`N|91$0lx<%B256d+8=cE8=z~Xr0Ejt z9b~EYSc(93+uOS62|M}-;WuxJ{epwup*(vWS^!VmYg!-!x3vS>V_8!nv;g&xBsp=v z*ni*%O0~HhHjp?KLivCxo|PGKmb{L;tnPfl90&IHs-DNWZ60|s{BapV5Gn#@hu<#q z^7P8nxDvYbb$xAgBc?&X2eWoKnGSjE4st+Fx}%o0*JO3m%G^1)Pb8j4D$@Zw*ZF@Y_xe_ zI;7|K3r2xJ;79Ae1#ojy;DbkUz~M4WL^HiAzFA@JnSBKP1gbt#`I>opis#9Ilb!~kf`)7 z)_Q9w_PZO~KaK?!RfHO`k0Gx4R0XjezCX^Us8z-H{o!M8=h}QoYvF&_67oy{uqPV)e f0WmQ#F`D=fBT8#u3G6ze00000NkvXXu0mjf)qF0Z delta 469 zcmX@b`Gk3bWIZzj1H;_yjcm{s1cqP=BzC5d6 z4-{f63GxeO=sY0%!uz<_tJOde&H|6fVg?31We{epSZZI!z`z*d>EaktaqI2%+j-3f zJZuSXR4m%R*WZ_P<1$e{UcX|t$=m(fUTgTT2AYO7aaLv}y{RD&QL)HKxzlKQ({s-JPa2=>RY4eaRK%h^} z#`tz$enX}c;|x`i22Uqe1NRM2PBX>%tzc5)SjKgO^BJduPX^m4wDtwOgM@hni+~5_R89a9;nW2JneIoQ>NbY-`(HR6Eu#^5PBM;)pGstlnTMC z(u{qkVuFl$+nMikF#Ne8=Pjjv%fj#ZncH(_b0?HeObF29I^%b1e+0w3X0< zP<-Fz>Ynq(PPeKxRi1`VK6s~Y#ot#&8I~`m{V(~I+k*favJ9Vw3y(q72hTCfmH<0XJC#|N2-d{KdL5LGzX5RO6-{=T^t~Z`v zYpBzHMn^_KIx=mV;PvhA$+?7rFuYv;0-yTGV-F{1zJDTTx8jphh6e3E=h2WowABF_ zHi%9b5+MXp0xItg9`>N0U`vUtk#JTg##11&>kq|?f%6DE z^G5z!S%2f5ViK}I-a!aJi~wKxPw?Qaq>;)Wsh=zM4LAEv5$WgF*p|=85W)wk2?)T4 z5CTr_6uIS|m4p6>;u(AkUt(qn-Dk5F^+)bfKYGA;&ZO;CjJR+AgwI5;xcwTZv zR6KA1xxIVsUBt#OpZgq9rGk9Hnr4^7W)o=W0)K{HeHm0LrES;Fe1@oAhZMvJU|*gi zGxwzuCB_>lHqM*akNJi#H+cQ^?%M*Mi z!GF(1e83)eF3%#WWpX#y;guf^MZfgEy>Jc!5L18wdHNU!^vTAT=b@e+MPNLu6}d|z0Zzw4B_~2nWbM_;V25RR9Jj3Q2TJ;ET0aVjsjj9YBhx> zPjVRY3`f~8l#1ABg6C@#iZB5(UKwbvuz%XzN319aKvV$+7r*jP?ir5`b#+H})_Z!` z>K>QjSs%*)<9a}n5UkxH%zT|kb=mHk=IYJrzL%!kPl5*_oPS|>%vW@lvp8G%xIXWhtbdTY zx?R9aQelwG{_JHOl5bq|@ckv9UB$QrAEe8?rLKSaQ*|0`hL{4TUmTrfMUOc_XZ;kJ zfO0uP|6qh7`?}{xmWkCWVfmKAjjP15g3@|HQYT_vENgFd*K_CEs{)Z2aPgJl$>tiy zILsv)MQ@W@97X zl@!J94O2Mra~kc2*eXycZgjB!++Mfee%XRZTL!FU3{N0NfJt(68wPLw-GqZEgb2i3 zAj$HKXM6vlmxM_3Ogfz~(123tk} zMgn2fx}9-Wtge!+1**S^=6{B`JKWAHD6!sp0EsXl<^o~rzsP2i4Om`Q*w%kjGx3>uY3#rR}XxK$M3R)M$@fFy=xJNuO- z^CnnvP|QeJy{)ja9B1a~x_7q%az(TqA_VmCORSJHn3WaP5(5$qIDexmi>?4##qCj*Zlai_z1E#HlgBX zePxLVAQ%J8o88gT5OcF$OGIy#v`chM@v~OX+@n<`b_2w-J~G<0-zTvx7A=dL3eM1Q zh+pSTH_DZ}&4bh}Tz`rj2CYAlvJH@sF}fq*r4wQh|BX^R8fI)U9hf9t+{|o;fp~ z>xv^GwhswaL0TaGW+=h%NPzzS5cLMx_kP{onM3FE8c*$M+GG5{NWZ%ac7@msY#IRu z<_8hxxED&oa{nuAiVOUo(IEg2IYbDcAP{+#9TBztdqoJ?d17fM()uLY2o9a6A3rYh kGno!L=%9lRI(SI&2XAM;0d_Z^%K!iX07*qoM6N<$f}^W}%m4rY delta 464 zcmaFIcb|EJWIZzj1H;_yjcYe8X zpnA5FAirRS)uK-eY_Gl06bFiM7I;J!Gcf2WgD_*oQu{In21b8R7srr_TW`Zp7admM z={e&p^ymNou!QZsvJWRF)E8xLR*x@B|7zBMoIT&QrJ;$TaQ33|Z+!xPYaDc3A{|0p zly?5yy16{y=my;ntgOxL^99cTd8W9Fr(j=$jibt%pR7j{9Stw&&R{)c$}B5-)=)vK zrc?2+GNWy0hX11?$?4LWJExj*>oK2ovV1!6hWR<)hWX-L#!c<}6^oclztsD?3-)vT zV7b}-z{uh5cmBPh2NNB6-Yhi#z2*J0HybuAympF@`=f!uUCssPCPgq5vqZLP(vW!+O=DB1m3Y6`V#qT-QuOZ zuObc)lv(~%bKfZsyS?jm1=YYUD=h@Hh=S<56b?a7`?{KuO(WXwH!CraOIUJ5ej^7-$ z{T+_HJ(4<7IPyArb@%FCeICBy$Yj+=68@3VE|J4Qm3~v@6FVGKC5dXD)Zw5J$&^oW zhlAo$s0yhZ4qA~~CH(Gi(9pE%%^wa2O-QeDWpFs?Tt+n}v%^8fv#O`r91hxhl2*xR*ma898|2XN>k6_pf}bzoJ?BRKn-f>a8T7o>i5PD2i>vOG;uhnV^fu* z8D2L;SgV^m9Mr6ZO5W1ppp(|nRyePa$3tC1(%9B2ZyUUR2($XPbvP(_JGBK#Rokm; zNNV3f#UQC~NA(&>gFC60Nb1*F#UiOo7j+Lw&AO^{NUG6IokUWl?&=7V+ z`5-B4FSP_oDSN9aNcv_C?SuCR;;i<4@w(uuRkNSNK}W2@{T&WkX(bwfs~84aH;`0j zpjwWk4_2c=4hQ*JzmU{?u<}LH538xC!$F&@cSx!-M9o1`xb^o?hl7S#fk;X=Of^H& zQY#!uIftwENLpcCLeh7u=m@;e(AH{-q+j?xEwFMU$p^oiGF$)Qxlk;gFa2YU!)v0W zc-{0A?+GQd^5FfW5_oT^B;J4e8|Q_x;(U=4=anLH&gme^0!>93p#~^BloDl%LQ(c; z1j;1kKv|{ZDAUviWu4yQnxM(JPAD0!ADY`r{oWGSEzD`I5;a4)fKg4<^F|H_wP~nA z>N^}%xSm>22k#xcsio@IM7<9itE+caao(X)6*U9(7hV2W{aL}`ptj}II@E_0Sw`h3 zjn@U8N~(>;@p~b%sLECZ^&xl^QVR;=`M?=B^*tZT02KdAb<5*$(6U_WYz~KmK4(*I zSy7*YM`rb3Mx1vz^rw274(As#|Do!q!Rv(ysn!0JxUL~CxyqFc?GUt2qSpL|dJ!TV zD!(`!G$=s@{zSQlls{D0Zw?0qeo>h|qm6~JAJyacsNbP!yb6Bna8QLe>hLRvgDSmL z=bodUg7(i;e4N8U3u0BtCn%o~{z%P?aX6?#w0ipx*J$ zuERkk?x>%)91gmCQ~BJ$bp{izs}9#34yt@r<-daW2hv?ui7z=E^y`9k9tnx9bm#Ez zz%5KwKkIN%?=xyyD9$^a4^hdG)cCYo8|-kqj8!R5)j?Lee0s4wC+|5+muw5p^3$XRJUZ?YH(I zX|Ht%NvEvqNP24hhTlyEtR_erYxyH7*2<69LH(^mNJ@&=PaUj-NJ@|QibhxukW>fn zDIKy3<2@%ID;LfQt+#UHoRP0p4Cj=NTg`C3DbAXVGCBb!L>z;Z>z4j?x+;5K}vj2-AB^?a5d*X>VN3=K$VSfI4E_bib2wmhiZNl`n}L2 zT9uDMe;!gjQjtjVf2_tnK^cMCu_{d*`j>FW+VB+D5A=Sfiap2qf_K)w7Y+vvd8tag zLVW_StR1f%4(k0z#t#qQV21P{$A*qo=;nX>( zTOt*Qq`tqYr%39aSlvfb<0R??k_snPOOW)%YM%`40|Z)`lB3;$Vb&!ixusAOkaWu` zloGEG##@(>lp~eui=_S5cO+F!t)?L<#B%(O7Yh}w5lGr;MIq^rG^!GkdRnuQw9`6| zq*yBfNg2|r0!S)r)k0Det2L53SY43R?f>YE$JEYhiKIqWO(d1KiXbV6l?q8;@!aT% zbrnfRtW8LoVGTr5bL$@@WwJiwy`tOJek3ih`XH%_l@UoVa6agywFF7Mtg=W-V%@+w zrHxj9B$cs#piIzdYZj84S{acPfpSHgt-eSqZhb&mq(EyDl4@Hik#q&+nHF2Ekd(!G zfbvfptZqnhvtHnup*@xtk}6uiA?XsXHCkbHMN$##GpUR2KeMJ6mpYWKzS_w!>f}f={R#qgrS^prZw$&O*eXVgw zT4ZfSQjm2ENiVI$c+V)ORUS#LEZ6(L5RYk>bpc7wtmHVaRM4u6q<+>sB<;0sBI%2j z17(P6TK$l;*a}3_V=E2HCRMO{A!(Tvilk3g0hD`cXU#`au!UybL4|SsP*-aOk}g?^ za1By<%M(d{Rs@pL4NnPNlDN)QC_POlG<29ku=xxMba_r29lmzf~3r7 zFR7SS6G`o?AxLtyyK8Yw`>Zoax@WyW(oZWj+IGrj6+}{Lt1^=6Sg!tus~^z@Unf`p z!qp#frR(b+_&3zdYJj8~Rs|%LwA_&7dS1Wdn4Eat6mP{K>6&#CN&eOvB+ayjBdN1h zA4z4boJdMyy~a5pS3l)Aj>+4afuz1x10)r)k|F5{$_TmoEvs=%L#?_<%3-}jd880) zIg)x=?np{(-9TBVjn)7pm9u_B(q&vbw94v+q(W9au0c9%O+=EbACnr#bOYBkt+YBK zDX$fWx`B3DLy=V0`i44&j$0FvRLv69RdmXljHIeo0_r?EW{pOYyY(LRC%O79gKbsPztGR61ylK~fbfF_JE#@uOu{XCxJ}K4I)jA=VNkb+bw%DT#F-V`tiJO-E9D zt2B~QT8}X9rlZypBn_}?A}PD|0poVMWceX!qSY2jrL9y*io=+nPFd@aG{$O;q%u}| zB*ni~H;{DDT7{(XR(B-Tw@M=^r(-`=PfNQ$(sBPq-ZLQ;UW2T41vZAjW`Z9)ET zuJ7-_*U8`7hopm6Ad*g6XOMK!x`Cv7Ruqz+T5pl`#S$c?z-y%pRvsi3vnnB}vDFz# zL#>%eT5km)>5BCnNl9>y=r5}(k~&%Ak>onxK{%!;%Ym{%`K@|L8e}a&(h=(+k`kjl zQW2{qlEzuSNV;TwLQ-y&dum{LA<5gijHDk{VO&?#&YFd!!`3q-Wx@4I4Xp7<+G{;Q zQdV5^)Wn*Gq@z|mk_w|9p&r(HB;B+AL_I`Jt+_}#XC+2GM%AqeNIGhLMUp$}LmF=F zMbc}l26rBfNqNy`WYF~@1WY&2qbN`t|95CRSNq*KrB%QXdA?cy@5=mdIMCj{M zYAX|xa$A2RsiajNNmZ@dNOJX~o8p*STCVX_2Rx=uR%ax2v0RTk;xV`M|-dm56bi)cp(q3x=lIB{Yk<`;_hNMbXK_vZYIdOjJrR5s8 z2IDcg#$JEC97fCs+Unu_+X&pn7 zYrNrwV`^%-#vO_9m>%GIqy5%wBz3Xek>nbOJjV4*u62S%IHtZ)y0Bo(oeAn76MIy!7EKvFNO5|Ywcu5}aFxMm^hRO(@s zMN$gu0qSJhWlcm zYb%nbTRo6e!^(>!*Sb>_+B`a8twqvk%L7TJtTae^iFTF3EMFu|wmKonHQ$&4$Mg>E zHC?i{BWZ@!6G=6#zmVjbcYTch0G+Y6A!)AViKJH6zevhsB}dXb^gHOj6^f)i)>V=u;wAj)lYP#Yu$7qzDJ8K*Y8<{ z$F$Dcgd|^U8-{UwY*XszwF}YsfbsWa|{cuWthmq_|$r9l}a zSGg>OW2#}bM3SpqyZYzT@HJXuc_YbneFWi{E?VJ8inBf#Um*h>I}+Zl}1uMt22_kthq>Xts5Q2F5ko45Tl#PR2{Sy>t2PHzE zg_2pRk@Sa^0ZCb`>`2OGVBv(JH1&*n; z)gDQmt!_x_Y4t(U0LwK$Jp_+wm^A`PBdyU$a;?LU!!eDwT>ZbvcuZ5QX-JxG{fDF( z)=VVLvSuS`&i^qNk7?fjF&~dDT;;44j;XoT1WApo`bes4)k0Ert16NzTNRO1 z!77KOvX<*QDTBvU+Hze-CGnU_SjCZ4)GC4`*E-DKIHm$tekA>6*WdSyLFQmhq&q=(i6B;B)aBk87f9Z9Zw z@Dh&cg5?^IoyB8v)tkXMrW4k2Bn4WBkhI_0jijB{RwVgY8v>i^)^0z>E@ujZ#x=Up0#sO{TL6U{DbBWdV0ID;eV}xgF-IVF;1SV z>7a^ds~UqtDmkcBa0Mgql)HltoGfirJXyj)*H0BO3WOANP{p(PjGGs8IjF;ptj3M- z3=S&yD6O&YbqWWi{+ig3WC=odGJQ3g7K#_jRQZKbq{U;Qg?%H88k27e)m?Gbn7H?x z(A{&Vji!%|3&s6BWUS4yPsr1Kr*X3B7NH}9HyOX?tQ9J`ZH3YD>|&w%G4qX5j@d%V z{+ezKs5)6_ey8z<=lIb=Y1fS~JdY0%njbmP7~t$Hl>Dz=M$g*ageLauWVBt-PAL9> zhjB5gnb6YIjg0#L))hK3xVll>x1!MV=zoleT*ZZQb}neV^35&uC@zb!qGURurc;v} znXihIGUoqk#1DVxq=sjn8ToQYJ8ALodq&gK*PY~(^@0)5Bh*P<4h0!uiHbhm+R#NNM=p zN$jM{4V(!SbMU)StjMPXx;;1E2>ALcf#TagH)`*XOCYzDPmI;wV-m=3Pn41IS7ZXk zHi$67=RHWE<`2RRk97AFD7o#8+N)gQapm>un(K)*b87+yhJ6UZ;q7Q=0lPXZMR_cj)n*_1%-7Hlv|-Cv(T z6$&V?Ve1m8{vI_bVtoSjO0m%>R(4YYU2n13$m;2vK)>c}F{W(UmOvi+b{a`8`6tlj z+j|YaxBC-l(ANN!?nuHQ_`UE^qj`j(dyLAPJv4z1zqp{PayDYw(=}@;Di8_-~I)p!!Q=jO~5n63D;V3!`@aw+ZC-_@nWB)At1W zZ>ZDATP%r_yq~5t9NvF8sb;%OMz23|IBCGyyvEyw1)OxbW>KS4s!~phiTlUcxvPSc z*3PJGTNZpSLphJa6r!>^IvPUygNj z(wL*2ji#HsIjPL>UPham{hV|x{U9UY!cZp#PZ?=MmK*1!%y%XmWrokd&&_cD{&k+Q za@eu zrcNn@ig!wB46d14sA-`z#`jFN=Cm?8N46I#(4ebvHK3PJ`#b}T zaekgcy9N_j*)l7e4+J)7a7HWEf(_Gzs%?~ zeTC3rk5xuM&DBEvGORV;Io1i)eXBep)(iDHzriSRXroY$|GbSsLpKW@sOoF1{(Xy3 z-UnL^w>dk6)|U4-ZYAC$RCE76!@F95(EV43jT>W*3Ej$n!sxv>Sg3r(Ge)B`=Y-A= zx@e62d_^c|{dL2u)-9p+FYXv+7ljLbZS}zDkTp`MQ{+R#5gaAdX?lz?yU!z`%l%cl z!H{vCw0bP$-bQt6|5#{#Cl%QBvCymOkBpMD9trK(6=Njb8za>HfI4v~Mkw1+ zkOWLPGqu#16SDy)$ znfJo*9q>{pw!tgoLee)vOJh~B(6{*eZ)N4?cgFR}@j{cPs$pH<3nlNaX3vZlDmPm> zcfS+*dh4x`=f@kN(Rr2BeJ#{r=}V*j(HBA$lRh`*wsI{#KOqCy@ zU;aok&W$!w9E=j$ljxz5y=bJ+VUGx7+N1|U8>Xp!{`ZBR>{C@E!i8o>tD>pGg%+k& z@qdO3eW-HJ7+mun>X&=+tghh&c zzvPQk^|S34Dwt0#Z?jKmc2`w(`CcKn3ww-WMRp6#?&@!3Ua?Ck_17Iny5c*8{18=?<9bf)u;(%BfkqhY5Xf8)^it_eA^U ziMfKoMsWKg4-DZDccF1ZrRpISl31%5$uo(X>Wc)H7w};rV}zOU=s&WxHR>2wqb{ zC^lblqo03Kp~mHk7>j}n36*K|x8Z)bfY8eBZbsWT`Gm%d{mV#qC9hC;`#gp`%_TI` zE2lB|w;VzT7iTlt=gErxUsma|C$o_wNoJvQ%Q70b@@GJQD1%fBNN=R7lTOI}Zd&8Z z=HG?B{*&5h7nV{eTl*A-&;I1-|0T!IBr|S=C&6`;1lL?*V^X5ugxYjWWc(K|PP*Ap zZI4NCQsk&##>*Q&ofOvZhtcrNcg)v(cV6xI&Di(xi<3SzQDF zPP!VQF#qhNGH$9*{fACkJxE<%{Lo3x09ES#L%hz1&Rz*>L2B#LQSHL zn!}=FC^9C-Nnsrx8SYgcJ85X1Cq}VaPn?vmK&r?leTyJXbi3P$w{ele>Son z{oA|8697yM|+kYea{R=x+|GbKV*__-Le?Z zc4rgnRy~IiGe4KmwA^`&U;Y0QdK!@5__t00puhY8))js9t}^~a zcjMyMo?_pob$?^2+aRIhoji>S%Z3W&%`n_3(7+4hZ7*3nX_WC#!e}9% z(&LP&y(b7=c|6hBl4%N_?-Y4kZ<_Hcbh=Q9&oc~GHw_n?osM|>`-!}p$P=lLl$bulDKJ`x+#S)(s z3O%8Uj5#ILy-cvt?{zTFYp_(`dfIs2HAJXfKeh6&P@%8~D)w=x(3N#(jF}#1g}gec zry0Y9f^w>*Zeb`dVHkU<9JD`8&!TgE_9&F zF{4Y`qZp$dm4*WYjg2{u2sy?cHkRK#By@LHfDsdSQ0OAYX_T?ye&gZxeL`0{?lp4# zu}A1~h`+HupTE$jyE~1Y^L7ZGs=nQD|FTsmrT-SAN)JDw8(DpgGuJi?Wtrt|T&%iD z=&!~bjGal=3oSdb)`%Rn2IKNI@?WXdM*Zq5g~EO;H?|yFCiGQc$84Rb6&=;$HRq6ZX0Ht9ynC!^K4I}OVhzZe`g$I zgqIm0H0FChV{~v|p=~j}jfvZP2^}2T)2RP%53Kj~5YLX?j0bLAg|=cWK{o(R@TZj8EE0`f6>BGO633es3cO?s*tX{aRr@tCidw*3uYOt%cB#G|i1G z5lw~W?QCLv&)r0*%iBgqzLO1w>Mm?xTy^X2Q#pCmTjg*oFO;UHa(#Y6M6L>0C$1nfBUJtG z6)>)?ApVE{HQL_(S7`W>ibj{Fl~9f=N%qv0jfsUS3w?-Ha!_jlDXT;tR6C+eX;QcqHku5ZYd20~e8 zH8dR6vHuT}tZZzIYT88TmTyzzO2=lX2b*E;vbmA0Ukjm3K`o8EBU)kpzLm@l^Dq`? zYa)M#i)6si%_}0x*3;-b{BF4_b~3H z?IqNxb8n+^x;{dwy7V>1?dpg2ub<5M*5CNI&Or1#2TH^j6_9=ratF z5Vv7QlOe-}+UFTzRPOI3bo#(ZBkt!Y?28yBJ8O?I(k&Q^vOHF1RvKs2o<3g4_u>S@ zC;cR$n(Z*(1JgE6F?z&KMLRcDVhc|<-0x1uxOlpxMcqrYyUsNHd}j&0Z93cNzI2Yz z$UAcl$CP>K=g*VDA@hxKNfrv#-J|^9EfP9dYKakie~D1u?8}URkYz%He_LV9+qnYu z*9xpftTbvaStaCuZMETQj~?V&E7YdrIwLC4dZBXLRY>dxA)mP$4UbZrgl^wa1;=~i z{A12BFXkSh;#u{pzYoR@KA6AuHR2EWVqb@^7MG}#`Z^BGj` zzxD`?Dxt1a+9MQ+-$@P6sAKq@R5)7g>9AXtG#+n&xgxpf98nO!S=PF$LD#f^!LercpjAB^Bq^NPr4eVvZ2EXOf z;oDN`UOknp{uH6rwDr3)yLMz@)FzQU0AXH=Wc%$F%8M+>%TM^dO=5;i`JFK|;Z61{!|^3>3OsPhE)^fPUrxY5Yaan>#=# z-xjs~-vL5xJyd}g{e@m79$+L|(_bk1psL)UztBIuR5<$C^fY6C<5S80LccXqc^>r> zs`EsGM+-$CR70AK z5xVwNIi`$7zhErJ0ppCD`^O25XgJ=8eLNoX5984WPcSNEn25S>BG&2E{LqP*51J?i zJ5DluhD{ban_`L)xM_;egmP1jbXTTgesHRI^_*sG|2|E~f6;VfYJvZR3LaPMn$Hl5 ziBn(3%@m6KW0tYhZx-tRS#sv^Y~!~^b5I87U~Hk{htI_|GgrKm%`@_@ohLM;_DEch(}z11ysGY>SQiyB4E;!tv21Mvq2IF|J*T`4)9#@G|VT zSte_ps>!0|Lf7-GFx>X75OR&rD_>tJrueRs=lC~KxK;VG^z&d6smYuIh*VfT7Fk`eYOkhqr1d^ zh`-ToioZ}F^tGt+Vs*-UH`ecWWAD@+<7kCFLXKeNaea?atG0WMg?;y8U-Vw=A5sIH zd$HdbUrW2sc)EO_&<8*DThaYOUn;7Xm^Y$z*VUMq{X!n`sub>vqW;()KqXeF4}J&m zI}ga=05$H+0imk5)s1!sF+M+tJyWX4n1h&aJcu%@rmZ@N_4R|8Ls5~3528PK5Mwaq ze)k~86FSGmd9DSYRYmM≺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 delta 181 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 AZvX%Q diff --git a/examples/starting-endless-runner/assets/StartingGround.png b/examples/starting-endless-runner/assets/StartingGround.png index 2a66b69a02b5edb6a6a4bcad3f4f10f259b5d5c2..d181229556ba7aa613300ce4062e7590851e4d06 100644 GIT binary patch delta 655 zcmbQoJco6HWIY=L1H-D!!h1l9v%n*=n1O*?7=#%aX3dcR3NjW4xjQkeJ16rJ$YDu$ z^mSxl*x1kgCy^D%=PPlIC<)F_D=AMbN@Z}%OwUc6w{coB0|S$|r;B4q1>@Tp(Rob) zBCh5gOi|e;Z{6#yX7(*EekowJ&~M@7j7$Cd>U)&*&5<1{o(Z>QE%L-egQopE$#&+sq1rw3 zw96XSX1&ZT{{;5_la=aKJLh@xm%H=?9buNqiiZ-W$Yz_x&REuI@oi(onU9~pwtWkg z&)Z$kep#cue6II@4dx@c`F|F=w74C1`ucSNUemladI{ZiqJ`-khT6_5S5x-Xru==LJ6zxKQn!i3G2er3tr zzWOzz&gpZ1_e~U2X}y_gxcK32EhR4XSFbFCN`6_juKH5nd-wRmyNb!{#LKwnH0;@S z``Zk~(5-dX7%QFK^7QAvTM)qQeQ@oU@0-#@D%smZVt<`#aCTm%ku`VTn#XTsgGygX z`|Iy3>kdPf^#^S1=*B%TqHW49CT25 z&LYuaAdsjq>7YYP;|VS{rEH0g7!DJLS%)21SQ8Zv0Yy2OC-U;R9B@$ij!UgG^8!nt fmc@G+_v^5ojCy3-y$YCs7&ts#{an^LB{Ts5>o^+4 delta 399 zcmV;A0dW4D1)c+t8Gi!+005o0f$RVP01Qw}R7HWZD4D`dlD$KTw>KgR0jK}~010qN zS#tmYE+YT{E+YYWr9XB600B5jL_t(|+U-|S7J@Jg3+?^yEGoj*CZkioc<|waX3a*) zx`^e+3pc?{kO<5HH$9r*9l;7eFPHfQM^6uX|I+*?;t@wj?|+|h#;5o0rYC69jJ-w^ z>~Q3Kx(JDb&lwmJegq)ix=SJNw1=Ro)>NBUb&GOBvRw?QlhMj#jA2t-57S7B`My1QOF|m0P#pN1``D~NLk{iNQ zo5wc?iF1OpqwItCJ_*D>4G+_ZLmlVU;~q6b5%FWW5caQZd7w6Y)pVg|>rA-Tt)aL! tmZ!#ZxZ!KochX!Z{Qk#s6a0hV0d}(x7s>EvasU7T00>D%PDHLkV1jh>v#$UE diff --git a/examples/starting-endless-runner/assets/StartingHazard.png b/examples/starting-endless-runner/assets/StartingHazard.png index a11b9cdd662a0b68d720e9ec4a40901a4ee43590..4bc8c990f9fac41b4847284b3f39ab4b00286431 100644 GIT binary patch literal 2639 zcmV-V3b6HwP)9MDo8M^12g=`g1Ro8Sk{dM>IeD8mM5FOwE2ROh1zCRcm8uE_}j|@?bppYc4UAq=g zRqadcYnpoN_U+q~^c?_#H+fseNztzHp`W+V)W5#EM3RsuVEpR%mm1Ni;Z#^7^=2mH z41MuMFiesd9v%+VIozKc1TSD?Gu|2sg(4(@I+BFSFJ)peppfIP+oeUWULAjs$tX9g zAp(Lsov!PA?=jY`lGn6(+kE`9zXUi~;L@)rh#6xd#+ZmnP0QVzn3xFBE)e$ZRIc;x zkoj-9Tuy9mw_6-1wDcMNc|j78CV+(x==-Z{tJLIeqIyq#(KR4KQJf$m#5!}Yl%J?1 zcq*Nyjg2^^`PWnLDG=ZwEkKfx@&W7s{c28I*N>miJg2R#E$TefNymDQ?I6Mc;U}C< z=YxgWg+VKV>)YT>o?H7If-?w5j6A_I?X=`(n63JvB zol4(LBolwmX0v{W;t+yit8=-;wPwL}XzF`|(?|)BB%}y%II`@Ow;5nT#Abv}M0;Di z5Z?TEGI@0?P&HNf0g&ZT=rl<}iU38a3+Nttb8EBgb?^$IffNDB zR4TCWp9QJ~nwy&G@CS!&jfqC1l+I*YNdi?9fHHV~e!f&}w*{oNNSN9l`#PPP)HZk+8#?;c%LXC}$BF4vJG0XSi>;0!s z57NTI{5MnuY!h(#^2lhN)A@+iZ$Q&DyTcJVgwU<6t%3v{ZEdYQ`z}GkyLa!3Lfvl2 z<+4MgqrVzDd-mgRo;{nkRUTU=013yBPyXaj{Qg1q!X6YocG2i$860I?A-73 zlh^AN8eoQ(1%}W%JRT3lqA^OPQe+jlwzIR-&$s{eg^L&H$&)9ROk&q^_|uV3$5l-W z8qY1iStcuhinlbk2!SdA-rw8kEjkB*p8fk7Sp~G{+}U&Qov1=H>9q3MZr!M|8URN( zaNx$y|2k#CT&m-}OMAoSipvq8p%z7zxH^bB55fPvinwy(x zWo1POX9cK7;6PD+=QV-ba>{5WlPG#a(RyxbzRPu{vCAvs?M8gVQo+;H)5CH75UVh2 zN-{yoM6#j)?d|Pi*2-aNX^FlKe@k|OMhxD?fN(maTr!(PK?AN_xiaW+d%ot~i^dBw znT*KuQ)z1s3DM!`k)y)lH8nMn-|uIw@UkhmN#PC#LZ^`s5MT_4WJ4TpH-e$|9p`pQ6SkwVWie?O+nQx?eU=n%1FpU%MpRCai8^McJnw@f3T3!-) zdO5h=^7wSLJ&ptsJDh#aK zo;k$)Y|dWJpO4EGuR+R-#wWZz9BeEWCr+=0|A7C5=ID+TBtQ$SIz*tWs|%W4Xrt#5 z;b!U|^<=4NdgtcmLdW|){3jRL!%Du^hZaBuS#?TwY0CiwAoGJSCf0QQTS>*(kxIuEa3?jBHg#PPU|q6?~j z_;8ng7~xs}4bOUTdV0Dd)0Se~?Cfk3Cehb_BFZ*#k=5E`)eHdC4hUmX?tIXB` zXZz8)k0P3?+`RqQV9?H3+gXlo5NuEzi#t9@fJ8GhA5t;X+}9ITUo{;j zpyx=>jHkhO>CWGRkI5#aK97I$$V7jZ?(Xx1Z1kcy~obnzkh$G zn(N4Gw5|oD2;hiS zusw5A^ScD1i4J8yHGr$k!bgbRI={7q)37j!eTmdEIVl2yLD63mB-Fd>X|LdPyeB{< zHB1tab%IbqK|rl+(h7_kz-f7yBp|&|k^w&uDAzhekEvW7`M}Q2wr?Wg}5ho(^R96 zp~!MLazlKp>y?Nw6R_{A#mV7}=xYTB1_sJ5a3~{q0goR)2=mV*wkubDF;y~z+V(kB`|YDgQx8Z2 xH6Z{Crq~pQU=G}hs=72izyS_$fCKC?{s&(%dwL7Sk6r)(002ovPDHLkV1n_`{1pHI delta 528 zcmX>vGLL0~WIZzj1H;_yjc(978H@Z4G~Wt671= zH_-dzzxCy-!)5DLr&XW3_c>cz(TasY>slwOvvfEmcZtq_6CS|R2)cT zV^CYOF<~zE4DRLs4<2oMC;E~v+W%qe>~a{(ZgcERKVw96F*O zTsXYmatasyn|$u)q>jK^Hr4(!USfw=R6J5SQ~TBK@ZIqD#%y1G3#CjvpDOblH(2Pch>(BkcIs=6bFPDqpL*-5pPzScU9s5v=HwoW z8pDSnk=wrQ*SfcFW)e(!G~w%* zvR`46-_t+5Q?DjHa&~>TGwIT4%d^vs?*%-V#%3P6XYFBe?wYL!r)yX3JbP0l+XkK65-=B diff --git a/examples/starting-endless-runner/assets/StartingPlayer.png b/examples/starting-endless-runner/assets/StartingPlayer.png index 472f7be2998459c3b5686c8c200d2d72c2046a97..89dd22451ba35db6f10af1b0c18e546774701362 100644 GIT binary patch literal 2978 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 XaJ7l|q5$ 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 FV1j1w)3N{n delta 627 zcmV-(0*w8$9DxOp8Gi!+006nq0-pc?05(ueR7Ff_aWyqHHa0dmI5;&mH8L_XHa0dg zGBP$cHa0dk|NsAAUtjX_^4{Lw#l^)&Mn->se~*ukx&LcZ0000AbW%=J0B>IZjxp~R zy4HO*rZ@lq010qNS#tmY3labT3lag+-G2N400G-cL_t(|+JD8F>ypAC4227X5~x;v z|ChDy&bnGlZe(`P@6Ii$Vi%H32h?z{` zA&({os!ck)jd&rN1@*kv&X;~w(7+>zIGK0^u?M`ITYobVd%(4ET6Mt2>z{${OJ<6^ zsnagq<8`6Sd|rA-EH^URbgQvHQ^O|E%x{@$LOs$9eVLZ5Rh; z*XQ!VgKQZc-=f2o!?Ed*2lmdfbz>*?te|XMb>FPA<&m<}EJWI#E9%{YoXZKS(#-Mi zEz^go>*bgJ1}U?hv%<;dkt!VPW>+x%4v0{XX>l^EM0ovUQ4t~&+a0mURTHt&FNy#F N002ovPDHLkV1nr&BUJzZ diff --git a/examples/starting-endless-runner/starting-endless-runner.json b/examples/starting-endless-runner/starting-endless-runner.json index 42a10d5b6..7e5ba4c7b 100644 --- a/examples/starting-endless-runner/starting-endless-runner.json +++ b/examples/starting-endless-runner/starting-endless-runner.json @@ -1,7 +1,7 @@ { "firstLayout": "", "gdVersion": { - "build": 272, + "build": 274, "major": 5, "minor": 6, "revision": 0 @@ -153,6 +153,16 @@ "name": "assets\\StartingHazard.png", "smoothed": true, "userAdded": false + }, + { + "file": "assets/Collision.wav", + "kind": "audio", + "metadata": "", + "name": "assets/Collision.wav", + "preloadAsMusic": false, + "preloadAsSound": false, + "preloadInCache": false, + "userAdded": false } ] }, @@ -185,7 +195,7 @@ "gridColor": 10401023, "gridAlpha": 0.8, "snap": false, - "zoomFactor": 0.5843784410741054, + "zoomFactor": 0.44819780454104513, "windowMask": false, "selectedLayer": "", "gameEditorMode": "instances-editor" @@ -438,14 +448,14 @@ { "angle": 0, "customSize": true, - "depth": 0.9375, - "height": 120, + "depth": 0.8125, + "height": 104, "keepRatio": true, "layer": "MobileControls", "locked": true, "name": "JumpButton", "persistentUuid": "58f2336d-aaa6-4549-be1f-64aeb618bcae", - "width": 120, + "width": 104, "x": 1110, "y": 576, "zOrder": 13, @@ -770,6 +780,7 @@ "adaptCollisionMaskAutomatically": false, "assetStoreId": "", "name": "Player", + "persistentUuid": "e0000e80-cd53-4d48-a622-9b4f1e4792f2", "type": "Sprite", "updateIfNotVisible": false, "variables": [], @@ -795,6 +806,10 @@ "xGrabTolerance": 10, "yGrabOffset": 0 }, + { + "name": "ShakeObject_PositionAngle", + "type": "ShakeObject::ShakeObject_PositionAngle" + }, { "isFolded": true, "name": "SmoothCamera", @@ -1184,6 +1199,25 @@ "TouchIsInside": false, "MouseIsInside": false, "Index": 2.0247e-320 + }, + { + "name": "ButtonScaleTween", + "type": "ButtonStates::ButtonScaleTween", + "Scale": "Scale", + "ButtonFSM": "ButtonFSM", + "Tween": "Tween", + "IdleScale": 1.3, + "FocusedScale": 1.25, + "FadeInDuration": 0.02, + "FadeOutDuration": 0.05, + "PressedScale": 1.2, + "FadeInEasing": "easeOutQuad", + "FadeOutEasing": "easeInQuad", + "PreviousState": "Idle" + }, + { + "name": "Tween", + "type": "Tween::TweenBehavior" } ], "animations": [ @@ -1383,9 +1417,60 @@ "", "" ] + }, + { + "type": { + "value": "BuiltinCommonInstructions::Once" + }, + "parameters": [] } ], "actions": [ + { + "type": { + "value": "PlaySound" + }, + "parameters": [ + "", + "assets/Collision.wav", + "", + "90", + "RandomFloatInRange(0.7,0.9)" + ] + }, + { + "type": { + "value": "ShakeObject::ShakeObject_PositionAngle::ShakeObject_PositionAngle" + }, + "parameters": [ + "Player", + "ShakeObject_PositionAngle", + "2", + "RandomWithStep(-5,5,10)", + "RandomWithStep(-5,5,10)", + "RandomWithStep(-5,5,10)", + "0.025", + "", + "" + ] + }, + { + "type": { + "value": "ChangeTimeScale" + }, + "parameters": [ + "", + "0.025" + ] + }, + { + "type": { + "value": "Wait" + }, + "parameters": [ + "0.01" + ] + }, { "type": { "value": "Scene" @@ -1638,6 +1723,10 @@ "name": "ButtonFSM", "type": "ButtonStates::ButtonFSM" }, + { + "name": "ButtonScaleTween", + "type": "ButtonStates::ButtonScaleTween" + }, { "name": "Effect", "type": "EffectCapability::EffectBehavior" @@ -1666,6 +1755,10 @@ "name": "Scale", "type": "ScalableCapability::ScalableBehavior" }, + { + "name": "ShakeObject_PositionAngle", + "type": "ShakeObject::ShakeObject_PositionAngle" + }, { "name": "SmoothCamera", "type": "SmoothCamera::SmoothCamera" @@ -1673,6 +1766,10 @@ { "name": "Text", "type": "TextContainerCapability::TextContainerBehavior" + }, + { + "name": "Tween", + "type": "Tween::TweenBehavior" } ] } @@ -14511,6 +14608,4479 @@ } ], "eventsBasedObjects": [] + }, + { + "author": "Tristan Rhodes (https://victrisgames.itch.io/)", + "category": "Visual effect", + "dimension": "2D", + "extensionNamespace": "", + "fullName": "Shake object", + "gdevelopVersion": ">=5.5.222", + "helpPath": "", + "iconUrl": "data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iVVRGLTgiPz48IURPQ1RZUEUgc3ZnIFBVQkxJQyAiLS8vVzNDLy9EVEQgU1ZHIDEuMS8vRU4iICJodHRwOi8vd3d3LnczLm9yZy9HcmFwaGljcy9TVkcvMS4xL0RURC9zdmcxMS5kdGQiPjxzdmcgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIiB4bWxuczp4bGluaz0iaHR0cDovL3d3dy53My5vcmcvMTk5OS94bGluayIgdmVyc2lvbj0iMS4xIiBpZD0ibWRpLWFycm93LWFsbCIgd2lkdGg9IjI0IiBoZWlnaHQ9IjI0IiB2aWV3Qm94PSIwIDAgMjQgMjQiPjxwYXRoIGQ9Ik0xMywxMUgxOEwxNi41LDkuNUwxNy45Miw4LjA4TDIxLjg0LDEyTDE3LjkyLDE1LjkyTDE2LjUsMTQuNUwxOCwxM0gxM1YxOEwxNC41LDE2LjVMMTUuOTIsMTcuOTJMMTIsMjEuODRMOC4wOCwxNy45Mkw5LjUsMTYuNUwxMSwxOFYxM0g2TDcuNSwxNC41TDYuMDgsMTUuOTJMMi4xNiwxMkw2LjA4LDguMDhMNy41LDkuNUw2LDExSDExVjZMOS41LDcuNUw4LjA4LDYuMDhMMTIsMi4xNkwxNS45Miw2LjA4TDE0LjUsNy41TDEzLDZWMTFaIiAvPjwvc3ZnPg==", + "name": "ShakeObject", + "previewIconUrl": "https://resources.gdevelop-app.com/assets/Icons/arrow-all.svg", + "shortDescription": "Shake objects with configurable translation, rotation, and scale intensity.", + "version": "1.6.2", + "description": [ + "Shake an object (position, angle or scale).", + "", + "It can be used for:", + "- Hit or impact", + "- Object slowly rocking back and forth (like a ship)", + "- Simulate engine vibration, earthquake, or pulsing", + "", + "The top-down RPG example uses this extension for damage animations ([open the project online](https://editor.gdevelop.io/?project=example://top-down-rpg))." + ], + "origin": { + "identifier": "ShakeObject", + "name": "gdevelop-extension-store" + }, + "tags": [ + "shaking", + "object", + "effect", + "shake", + "scale", + "position", + "rotate", + "angle" + ], + "authorIds": [ + "gqDaZjCfevOOxBYkK6zlhtZnXCg1" + ], + "dependencies": [], + "globalVariables": [], + "sceneVariables": [], + "eventsFunctions": [], + "eventsFunctionsFolderStructure": { + "folderName": "__ROOT" + }, + "eventsBasedBehaviors": [ + { + "description": "Shake objects with translation and rotation.", + "fullName": "Shake object (position, angle)", + "helpPath": "", + "iconUrl": "", + "name": "ShakeObject_PositionAngle", + "objectType": "", + "previewIconUrl": "", + "eventsFunctions": [ + { + "description": "Shake an object, using one or more ways to shake (position, angle). Make sure to \"Stop shaking\" before starting a new shake if it uses different parameters. ", + "fullName": "Shake object (position, angle)", + "functionType": "Action", + "name": "ShakeObject_PositionAngle", + "sentence": "Shake object _PARAM0_ for _PARAM2_ seconds. Modify position amplitude _PARAM3_ on X axis and _PARAM4_ on Y axis, angle rotation amplitude _PARAM5_. Wait _PARAM6_ seconds between shakes. Keep shaking until stopped: _PARAM7_", + "events": [ + { + "colorB": 228, + "colorG": 176, + "colorR": 74, + "creationTime": 0, + "name": "Shake Object", + "source": "", + "type": "BuiltinCommonInstructions::Group", + "events": [ + { + "type": "BuiltinCommonInstructions::Comment", + "color": { + "b": 109, + "g": 230, + "r": 255, + "textB": 0, + "textG": 0, + "textR": 0 + }, + "comment": "Start/Reset duration timer" + }, + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [], + "actions": [ + { + "type": { + "value": "ResetObjectTimer" + }, + "parameters": [ + "Object", + "\"__ShakeObject_DurationTimer\"" + ] + } + ] + }, + { + "type": "BuiltinCommonInstructions::Comment", + "color": { + "b": 109, + "g": 230, + "r": 255, + "textB": 0, + "textG": 0, + "textR": 0 + }, + "comment": "Pass input parameters to global variables so that onScenePostEvents can use them" + }, + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [], + "actions": [ + { + "type": { + "value": "ModVarObjet" + }, + "parameters": [ + "Object", + "__ShakeObject_Duration", + "=", + "Duration" + ] + }, + { + "type": { + "value": "ModVarObjet" + }, + "parameters": [ + "Object", + "__ShakeObject_PowerX", + "=", + "PowerX" + ] + }, + { + "type": { + "value": "ModVarObjet" + }, + "parameters": [ + "Object", + "__ShakeObject_PowerY", + "=", + "PowerY" + ] + }, + { + "type": { + "value": "ModVarObjet" + }, + "parameters": [ + "Object", + "__ShakeObject_PowerAngle", + "=", + "PowerAngle" + ] + }, + { + "type": { + "value": "ModVarObjet" + }, + "parameters": [ + "Object", + "__ShakeObject_TimeBetweenShakes", + "=", + "TimeBetweenShakes" + ] + } + ] + }, + { + "type": "BuiltinCommonInstructions::Comment", + "color": { + "b": 109, + "g": 230, + "r": 255, + "textB": 0, + "textG": 0, + "textR": 0 + }, + "comment": "Determine if the shake should keep going until stopped" + }, + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [], + "actions": [ + { + "type": { + "value": "SetObjectVariableAsBoolean" + }, + "parameters": [ + "Object", + "__ShakeObject_ShakeForever", + "False" + ] + } + ] + }, + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [ + { + "type": { + "value": "BooleanVariable" + }, + "parameters": [ + "ShakeForever", + "True", + "" + ] + } + ], + "actions": [ + { + "type": { + "value": "SetObjectVariableAsBoolean" + }, + "parameters": [ + "Object", + "__ShakeObject_ShakeForever", + "True" + ] + } + ] + }, + { + "type": "BuiltinCommonInstructions::Comment", + "color": { + "b": 109, + "g": 230, + "r": 255, + "textB": 0, + "textG": 0, + "textR": 0 + }, + "comment": "Add default values if none were provided" + }, + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [ + { + "type": { + "value": "VarObjet" + }, + "parameters": [ + "Object", + "__ShakeObject_Duration", + "=", + "0" + ] + } + ], + "actions": [ + { + "type": { + "value": "ModVarObjet" + }, + "parameters": [ + "Object", + "__ShakeObject_Duration", + "=", + "0.5" + ] + } + ] + }, + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [ + { + "type": { + "value": "VarObjet" + }, + "parameters": [ + "Object", + "__ShakeObject_TimeBetweenShakes", + "=", + "0" + ] + } + ], + "actions": [ + { + "type": { + "value": "ModVarObjet" + }, + "parameters": [ + "Object", + "__ShakeObject_TimeBetweenShakes", + "=", + "0.08" + ] + } + ] + }, + { + "type": "BuiltinCommonInstructions::Comment", + "color": { + "b": 109, + "g": 230, + "r": 255, + "textB": 0, + "textG": 0, + "textR": 0 + }, + "comment": "If duration is less than a single shake, increase duration to make 1 full shake" + }, + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [ + { + "type": { + "value": "VarObjet" + }, + "parameters": [ + "Object", + "__ShakeObject_Duration", + "<", + "Object.Variable(__ShakeObject_TimeBetweenShakes)" + ] + } + ], + "actions": [ + { + "type": { + "value": "ModVarObjet" + }, + "parameters": [ + "Object", + "__ShakeObject_Duration", + "=", + "Object.Variable(__ShakeObject_TimeBetweenShakes)" + ] + } + ] + }, + { + "type": "BuiltinCommonInstructions::Comment", + "color": { + "b": 109, + "g": 230, + "r": 255, + "textB": 0, + "textG": 0, + "textR": 0 + }, + "comment": "Detect initial shake" + }, + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [ + { + "type": { + "value": "VarObjet" + }, + "parameters": [ + "Object", + "__ShakeObject_ShakeInProgress", + "=", + "0" + ] + } + ], + "actions": [ + { + "type": { + "value": "ModVarObjet" + }, + "parameters": [ + "Object", + "__ShakeObject_InitialShake", + "=", + "1" + ] + } + ] + }, + { + "type": "BuiltinCommonInstructions::Comment", + "color": { + "b": 109, + "g": 230, + "r": 255, + "textB": 0, + "textG": 0, + "textR": 0 + }, + "comment": "Initiate the onScenePostEvents function" + }, + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [], + "actions": [ + { + "type": { + "value": "ModVarObjet" + }, + "parameters": [ + "Object", + "__ShakeObject_ShakeInProgress", + "=", + "1" + ] + } + ] + } + ], + "parameters": [] + } + ], + "parameters": [ + { + "description": "Object", + "name": "Object", + "type": "object" + }, + { + "description": "Behavior", + "name": "Behavior", + "supplementaryInformation": "ShakeObject::ShakeObject_PositionAngle", + "type": "behavior" + }, + { + "description": "Duration of shake (in seconds) (Default: 0.5) ", + "name": "Duration", + "type": "expression" + }, + { + "description": "Amplitude of postion shake in X direction (in pixels) (For example: 5)", + "name": "PowerX", + "type": "expression" + }, + { + "description": "Amplitude of position shake in Y direction (in pixels) (For example: 5)", + "longDescription": "Use a negative number to make the single-shake move in the opposite direction.", + "name": "PowerY", + "type": "expression" + }, + { + "description": "Amplitude of angle rotation shake (in degrees) (For example: 5)", + "name": "PowerAngle", + "type": "expression" + }, + { + "description": "Amount of time between shakes (in seconds) (Default: 0.08)", + "longDescription": "For a single-shake effect, set it to the same value as \"Duration\".", + "name": "TimeBetweenShakes", + "type": "expression" + }, + { + "description": "Keep shaking until stopped", + "longDescription": "Duration value will be ignored", + "name": "ShakeForever", + "type": "yesorno" + } + ], + "objectGroups": [] + }, + { + "fullName": "", + "functionType": "Action", + "name": "doStepPostEvents", + "sentence": "", + "events": [ + { + "colorB": 228, + "colorG": 176, + "colorR": 74, + "creationTime": 0, + "name": "Shake Object", + "source": "", + "type": "BuiltinCommonInstructions::Group", + "events": [ + { + "colorB": 228, + "colorG": 176, + "colorR": 74, + "creationTime": 0, + "name": "Start shaking", + "source": "", + "type": "BuiltinCommonInstructions::Group", + "events": [ + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [ + { + "type": { + "value": "VarObjet" + }, + "parameters": [ + "Object", + "__ShakeObject_ShakeInProgress", + "=", + "1" + ] + } + ], + "actions": [], + "events": [ + { + "type": "BuiltinCommonInstructions::Comment", + "color": { + "b": 109, + "g": 230, + "r": 255, + "textB": 0, + "textG": 0, + "textR": 0 + }, + "comment": "Keep object shaking forever (if desired)" + }, + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [ + { + "type": { + "value": "ObjectVariableAsBoolean" + }, + "parameters": [ + "Object", + "__ShakeObject_ShakeForever", + "True" + ] + } + ], + "actions": [ + { + "type": { + "value": "ModVarObjet" + }, + "parameters": [ + "Object", + "__ShakeObject_Duration", + "=", + "100" + ] + }, + { + "type": { + "value": "ResetObjectTimer" + }, + "parameters": [ + "Object", + "\"__ShakeObject_DurationTimer\"" + ] + } + ] + }, + { + "colorB": 228, + "colorG": 176, + "colorR": 74, + "creationTime": 0, + "name": "Calculate movement of the shake", + "source": "", + "type": "BuiltinCommonInstructions::Group", + "events": [ + { + "type": "BuiltinCommonInstructions::Comment", + "color": { + "b": 109, + "g": 230, + "r": 255, + "textB": 0, + "textG": 0, + "textR": 0 + }, + "comment": "Run once before every shake movement" + }, + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [ + { + "type": { + "value": "BuiltinCommonInstructions::Or" + }, + "parameters": [], + "subInstructions": [ + { + "type": { + "value": "VarObjet" + }, + "parameters": [ + "Object", + "__ShakeObject_InitialShake", + "=", + "1" + ] + }, + { + "type": { + "value": "ObjectTimer" + }, + "parameters": [ + "Object", + "\"__ShakeObject_ShakeTimer\"", + "Object.Variable(__ShakeObject_TimeBetweenShakes)" + ] + } + ] + } + ], + "actions": [ + { + "type": { + "value": "ResetObjectTimer" + }, + "parameters": [ + "Object", + "\"__ShakeObject_ShakeTimer\"" + ] + }, + { + "type": { + "value": "ModVarObjet" + }, + "parameters": [ + "Object", + "__ShakeObject_ShakeCounter", + "+", + "1" + ] + } + ], + "events": [ + { + "colorB": 228, + "colorG": 176, + "colorR": 74, + "creationTime": 0, + "name": "Correct for drift and reset drift tracking variables", + "source": "", + "type": "BuiltinCommonInstructions::Group", + "events": [ + { + "type": "BuiltinCommonInstructions::Comment", + "color": { + "b": 109, + "g": 230, + "r": 255, + "textB": 0, + "textG": 0, + "textR": 0 + }, + "comment": "Move to correct any drift from previous shake" + }, + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [ + { + "type": { + "value": "BuiltinCommonInstructions::Or" + }, + "parameters": [], + "subInstructions": [ + { + "type": { + "value": "VarObjet" + }, + "parameters": [ + "Object", + "__ShakeObject_PowerX", + "!=", + "0" + ] + }, + { + "type": { + "value": "VarObjet" + }, + "parameters": [ + "Object", + "__ShakeObject_PowerY", + "!=", + "0" + ] + } + ] + } + ], + "actions": [ + { + "type": { + "value": "SetX" + }, + "parameters": [ + "Object", + "-", + "Object.Variable(__ShakeObject_DisplacementTravelledX)" + ] + }, + { + "type": { + "value": "SetY" + }, + "parameters": [ + "Object", + "-", + "Object.Variable(__ShakeObject_DisplacementTravelledY)" + ] + } + ] + }, + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [ + { + "type": { + "value": "VarObjet" + }, + "parameters": [ + "Object", + "__ShakeObject_PowerAngle", + "!=", + "0" + ] + } + ], + "actions": [ + { + "type": { + "value": "SetAngle" + }, + "parameters": [ + "Object", + "-", + "Object.Variable(__ShakeObject_AngleTravelled)" + ] + } + ] + }, + { + "type": "BuiltinCommonInstructions::Comment", + "color": { + "b": 109, + "g": 230, + "r": 255, + "textB": 0, + "textG": 0, + "textR": 0 + }, + "comment": "Reset drift detection variables" + }, + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [], + "actions": [ + { + "type": { + "value": "ModVarObjet" + }, + "parameters": [ + "Object", + "__ShakeObject_DisplacementTravelledX", + "=", + "0" + ] + }, + { + "type": { + "value": "ModVarObjet" + }, + "parameters": [ + "Object", + "__ShakeObject_DisplacementTravelledY", + "=", + "0" + ] + }, + { + "type": { + "value": "ModVarObjet" + }, + "parameters": [ + "Object", + "__ShakeObject_AngleTravelled", + "=", + "0" + ] + } + ] + } + ], + "parameters": [] + }, + { + "type": "BuiltinCommonInstructions::Comment", + "color": { + "b": 109, + "g": 230, + "r": 255, + "textB": 0, + "textG": 0, + "textR": 0 + }, + "comment": "Calculate Position Shake" + }, + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [ + { + "type": { + "value": "BuiltinCommonInstructions::Or" + }, + "parameters": [], + "subInstructions": [ + { + "type": { + "value": "VarObjet" + }, + "parameters": [ + "Object", + "__ShakeObject_PowerX", + "!=", + "0" + ] + }, + { + "type": { + "value": "VarObjet" + }, + "parameters": [ + "Object", + "__ShakeObject_PowerY", + "!=", + "0" + ] + } + ] + } + ], + "actions": [], + "events": [ + { + "type": "BuiltinCommonInstructions::Comment", + "color": { + "b": 109, + "g": 230, + "r": 255, + "textB": 0, + "textG": 0, + "textR": 0 + }, + "comment": "PositionDisplacement = (DesiredDuration - RunningTimer) / DesiredDuration * Amplitude * [-1 or 1]" + }, + { + "type": "BuiltinCommonInstructions::Comment", + "color": { + "b": 109, + "g": 230, + "r": 255, + "textB": 0, + "textG": 0, + "textR": 0 + }, + "comment": "Make initial shake NOT random so users can set a direction for a one-shake effect" + }, + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [ + { + "type": { + "value": "VarObjet" + }, + "parameters": [ + "Object", + "__ShakeObject_InitialShake", + "=", + "1" + ] + } + ], + "actions": [], + "events": [ + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [ + { + "type": { + "value": "VarObjet" + }, + "parameters": [ + "Object", + "__ShakeObject_PowerX", + "!=", + "0" + ] + } + ], + "actions": [ + { + "type": { + "value": "ModVarObjet" + }, + "parameters": [ + "Object", + "__ShakeObject_DisplacementX", + "=", + "(Object.Variable(__ShakeObject_Duration) - Object.ObjectTimerElapsedTime(\"__ShakeObject_DurationTimer\")) / Object.Variable(__ShakeObject_Duration) * Object.Variable(__ShakeObject_PowerX)" + ] + } + ] + }, + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [ + { + "type": { + "value": "VarObjet" + }, + "parameters": [ + "Object", + "__ShakeObject_PowerY", + "!=", + "0" + ] + } + ], + "actions": [ + { + "type": { + "value": "ModVarObjet" + }, + "parameters": [ + "Object", + "__ShakeObject_DisplacementY", + "=", + "(Object.Variable(__ShakeObject_Duration) - Object.ObjectTimerElapsedTime(\"__ShakeObject_DurationTimer\")) / Object.Variable(__ShakeObject_Duration) * Object.Variable(__ShakeObject_PowerY)" + ] + } + ] + } + ] + }, + { + "type": "BuiltinCommonInstructions::Comment", + "color": { + "b": 109, + "g": 230, + "r": 255, + "textB": 0, + "textG": 0, + "textR": 0 + }, + "comment": "After initial shake pick a random direction" + }, + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [ + { + "type": { + "value": "VarObjet" + }, + "parameters": [ + "Object", + "__ShakeObject_InitialShake", + "=", + "0" + ] + } + ], + "actions": [], + "events": [ + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [ + { + "type": { + "value": "VarObjet" + }, + "parameters": [ + "Object", + "__ShakeObject_PowerX", + "!=", + "0" + ] + } + ], + "actions": [ + { + "type": { + "value": "ModVarObjet" + }, + "parameters": [ + "Object", + "__ShakeObject_DisplacementX", + "=", + "(Object.Variable(__ShakeObject_Duration) - Object.ObjectTimerElapsedTime(\"__ShakeObject_DurationTimer\")) / Object.Variable(__ShakeObject_Duration) * Object.Variable(__ShakeObject_PowerX) * RandomWithStep(-1, 1, 2)" + ] + } + ] + }, + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [ + { + "type": { + "value": "VarObjet" + }, + "parameters": [ + "Object", + "__ShakeObject_PowerY", + "!=", + "0" + ] + } + ], + "actions": [ + { + "type": { + "value": "ModVarObjet" + }, + "parameters": [ + "Object", + "__ShakeObject_DisplacementY", + "=", + "(Object.Variable(__ShakeObject_Duration) - Object.ObjectTimerElapsedTime(\"__ShakeObject_DurationTimer\")) / Object.Variable(__ShakeObject_Duration) * Object.Variable(__ShakeObject_PowerY) * RandomWithStep(-1, 1, 2)" + ] + } + ] + } + ] + } + ] + }, + { + "type": "BuiltinCommonInstructions::Comment", + "color": { + "b": 109, + "g": 230, + "r": 255, + "textB": 0, + "textG": 0, + "textR": 0 + }, + "comment": "Calculate Rotation (angle) shake " + }, + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [ + { + "type": { + "value": "VarObjet" + }, + "parameters": [ + "Object", + "__ShakeObject_PowerAngle", + "!=", + "0" + ] + } + ], + "actions": [], + "events": [ + { + "type": "BuiltinCommonInstructions::Comment", + "color": { + "b": 109, + "g": 230, + "r": 255, + "textB": 0, + "textG": 0, + "textR": 0 + }, + "comment": "Every \"even\" shake, rotate clockwise" + }, + { + "type": "BuiltinCommonInstructions::Comment", + "color": { + "b": 109, + "g": 230, + "r": 255, + "textB": 0, + "textG": 0, + "textR": 0 + }, + "comment": "AngleDisplacement = (DesiredDuration - RunningTimer) / DesiredDuration * Amplitude" + }, + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [ + { + "type": { + "value": "BuiltinCommonInstructions::CompareNumbers" + }, + "parameters": [ + "mod(Object.Variable(__ShakeObject_ShakeCounter),2)", + "=", + "0" + ] + } + ], + "actions": [ + { + "type": { + "value": "ModVarObjet" + }, + "parameters": [ + "Object", + "__ShakeObject_DisplacementAngle", + "=", + "(Object.Variable(__ShakeObject_Duration) - Object.ObjectTimerElapsedTime(\"__ShakeObject_DurationTimer\")) / Object.Variable(__ShakeObject_Duration) * Object.Variable(__ShakeObject_PowerAngle)" + ] + } + ] + }, + { + "type": "BuiltinCommonInstructions::Comment", + "color": { + "b": 109, + "g": 230, + "r": 255, + "textB": 0, + "textG": 0, + "textR": 0 + }, + "comment": "Every \"odd\" shake, rotate counter-clockwise" + }, + { + "type": "BuiltinCommonInstructions::Comment", + "color": { + "b": 109, + "g": 230, + "r": 255, + "textB": 0, + "textG": 0, + "textR": 0 + }, + "comment": "AngleDisplacement = (DesiredDuration - RunningTimer) / DesiredDuration * Amplitude" + }, + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [ + { + "type": { + "value": "BuiltinCommonInstructions::CompareNumbers" + }, + "parameters": [ + "mod(Object.Variable(__ShakeObject_ShakeCounter),2)", + "=", + "1" + ] + } + ], + "actions": [ + { + "type": { + "value": "ModVarObjet" + }, + "parameters": [ + "Object", + "__ShakeObject_DisplacementAngle", + "=", + "-1 *(Object.Variable(__ShakeObject_Duration) - Object.ObjectTimerElapsedTime(\"__ShakeObject_DurationTimer\")) / Object.Variable(__ShakeObject_Duration) * Object.Variable(__ShakeObject_PowerAngle)" + ] + } + ] + } + ] + }, + { + "type": "BuiltinCommonInstructions::Comment", + "color": { + "b": 109, + "g": 230, + "r": 255, + "textB": 0, + "textG": 0, + "textR": 0 + }, + "comment": "Save that initial shake has been processed" + }, + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [], + "actions": [ + { + "type": { + "value": "ModVarObjet" + }, + "parameters": [ + "Object", + "__ShakeObject_InitialShake", + "=", + "0" + ] + } + ] + } + ] + } + ], + "parameters": [] + }, + { + "colorB": 228, + "colorG": 176, + "colorR": 74, + "creationTime": 0, + "name": "Move object", + "source": "", + "type": "BuiltinCommonInstructions::Group", + "events": [ + { + "type": "BuiltinCommonInstructions::Comment", + "color": { + "b": 109, + "g": 230, + "r": 255, + "textB": 0, + "textG": 0, + "textR": 0 + }, + "comment": "Calculate the fraction of shake that occured during this frame" + }, + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [], + "actions": [ + { + "type": { + "value": "ModVarObjet" + }, + "parameters": [ + "Object", + "__ShakeObject_PercentTimeElapsedThisFrame", + "=", + "min(1,TimeDelta()/Object.Variable(__ShakeObject_TimeBetweenShakes))" + ] + } + ] + }, + { + "type": "BuiltinCommonInstructions::Comment", + "color": { + "b": 109, + "g": 230, + "r": 255, + "textB": 0, + "textG": 0, + "textR": 0 + }, + "comment": "Increase change for the first half of the shake (move away from original values)" + }, + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [ + { + "type": { + "inverted": true, + "value": "ObjectTimer" + }, + "parameters": [ + "Object", + "\"__ShakeObject_ShakeTimer\"", + "Object.Variable(__ShakeObject_TimeBetweenShakes)/2" + ] + } + ], + "actions": [], + "events": [ + { + "type": "BuiltinCommonInstructions::Comment", + "color": { + "b": 109, + "g": 230, + "r": 255, + "textB": 0, + "textG": 0, + "textR": 0 + }, + "comment": "Change position" + }, + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [ + { + "type": { + "value": "BuiltinCommonInstructions::Or" + }, + "parameters": [], + "subInstructions": [ + { + "type": { + "value": "VarObjet" + }, + "parameters": [ + "Object", + "__ShakeObject_PowerX", + "!=", + "0" + ] + }, + { + "type": { + "value": "VarObjet" + }, + "parameters": [ + "Object", + "__ShakeObject_PowerY", + "!=", + "0" + ] + } + ] + } + ], + "actions": [ + { + "type": { + "value": "SetX" + }, + "parameters": [ + "Object", + "+", + "round(1024 * Object.Variable(__ShakeObject_DisplacementX) * Object.Variable(__ShakeObject_PercentTimeElapsedThisFrame)) / 1024" + ] + }, + { + "type": { + "value": "SetY" + }, + "parameters": [ + "Object", + "+", + "round(1024 * Object.Variable(__ShakeObject_DisplacementY) * Object.Variable(__ShakeObject_PercentTimeElapsedThisFrame)) / 1024" + ] + } + ], + "events": [ + { + "type": "BuiltinCommonInstructions::Comment", + "color": { + "b": 109, + "g": 230, + "r": 255, + "textB": 0, + "textG": 0, + "textR": 0 + }, + "comment": "Save movement to calculate drift" + }, + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [], + "actions": [ + { + "type": { + "value": "ModVarObjet" + }, + "parameters": [ + "Object", + "__ShakeObject_DisplacementTravelledX", + "+", + "round(1024 * Object.Variable(__ShakeObject_DisplacementX) * Object.Variable(__ShakeObject_PercentTimeElapsedThisFrame)) / 1024" + ] + }, + { + "type": { + "value": "ModVarObjet" + }, + "parameters": [ + "Object", + "__ShakeObject_DisplacementTravelledY", + "+", + "round(1024 * Object.Variable(__ShakeObject_DisplacementY) * Object.Variable(__ShakeObject_PercentTimeElapsedThisFrame)) / 1024" + ] + } + ] + } + ] + }, + { + "type": "BuiltinCommonInstructions::Comment", + "color": { + "b": 109, + "g": 230, + "r": 255, + "textB": 0, + "textG": 0, + "textR": 0 + }, + "comment": "Change angle" + }, + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [ + { + "type": { + "value": "BuiltinCommonInstructions::CompareNumbers" + }, + "parameters": [ + "Object.Variable(__ShakeObject_PowerAngle)", + "!=", + "0" + ] + } + ], + "actions": [ + { + "type": { + "value": "SetAngle" + }, + "parameters": [ + "Object", + "+", + "round(1024 * Object.Variable(__ShakeObject_DisplacementAngle) * Object.Variable(__ShakeObject_PercentTimeElapsedThisFrame)) / 1024" + ] + } + ], + "events": [ + { + "type": "BuiltinCommonInstructions::Comment", + "color": { + "b": 109, + "g": 230, + "r": 255, + "textB": 0, + "textG": 0, + "textR": 0 + }, + "comment": "Save movement to calculate drift" + }, + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [], + "actions": [ + { + "type": { + "value": "ModVarObjet" + }, + "parameters": [ + "Object", + "__ShakeObject_AngleTravelled", + "+", + "round(1024 * Object.Variable(__ShakeObject_DisplacementAngle) * Object.Variable(__ShakeObject_PercentTimeElapsedThisFrame)) / 1024" + ] + } + ] + } + ] + } + ] + }, + { + "type": "BuiltinCommonInstructions::Comment", + "color": { + "b": 109, + "g": 230, + "r": 255, + "textB": 0, + "textG": 0, + "textR": 0 + }, + "comment": "Decrease change the second half of the shake (return to original position)" + }, + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [ + { + "type": { + "value": "ObjectTimer" + }, + "parameters": [ + "Object", + "\"__ShakeObject_ShakeTimer\"", + "Object.Variable(__ShakeObject_TimeBetweenShakes)/2" + ] + } + ], + "actions": [], + "events": [ + { + "type": "BuiltinCommonInstructions::Comment", + "color": { + "b": 109, + "g": 230, + "r": 255, + "textB": 0, + "textG": 0, + "textR": 0 + }, + "comment": "Change position" + }, + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [ + { + "type": { + "value": "BuiltinCommonInstructions::Or" + }, + "parameters": [], + "subInstructions": [ + { + "type": { + "value": "VarObjet" + }, + "parameters": [ + "Object", + "__ShakeObject_PowerX", + "!=", + "0" + ] + }, + { + "type": { + "value": "VarObjet" + }, + "parameters": [ + "Object", + "__ShakeObject_PowerY", + "!=", + "0" + ] + } + ] + } + ], + "actions": [ + { + "type": { + "value": "SetX" + }, + "parameters": [ + "Object", + "-", + "round(1024 * Object.Variable(__ShakeObject_DisplacementX) * Object.Variable(__ShakeObject_PercentTimeElapsedThisFrame)) / 1024" + ] + }, + { + "type": { + "value": "SetY" + }, + "parameters": [ + "Object", + "-", + "round(1024 * Object.Variable(__ShakeObject_DisplacementY) * Object.Variable(__ShakeObject_PercentTimeElapsedThisFrame)) / 1024" + ] + } + ], + "events": [ + { + "type": "BuiltinCommonInstructions::Comment", + "color": { + "b": 109, + "g": 230, + "r": 255, + "textB": 0, + "textG": 0, + "textR": 0 + }, + "comment": "Save movement to calculate drift" + }, + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [], + "actions": [ + { + "type": { + "value": "ModVarObjet" + }, + "parameters": [ + "Object", + "__ShakeObject_DisplacementTravelledX", + "-", + "round(1024 * Object.Variable(__ShakeObject_DisplacementX) * Object.Variable(__ShakeObject_PercentTimeElapsedThisFrame)) / 1024" + ] + }, + { + "type": { + "value": "ModVarObjet" + }, + "parameters": [ + "Object", + "__ShakeObject_DisplacementTravelledY", + "-", + "round(1024 * Object.Variable(__ShakeObject_DisplacementY) * Object.Variable(__ShakeObject_PercentTimeElapsedThisFrame)) / 1024" + ] + } + ] + } + ] + }, + { + "type": "BuiltinCommonInstructions::Comment", + "color": { + "b": 109, + "g": 230, + "r": 255, + "textB": 0, + "textG": 0, + "textR": 0 + }, + "comment": "Change angle" + }, + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [ + { + "type": { + "value": "BuiltinCommonInstructions::CompareNumbers" + }, + "parameters": [ + "Object.Variable(__ShakeObject_PowerAngle)", + "!=", + "0" + ] + } + ], + "actions": [ + { + "type": { + "value": "SetAngle" + }, + "parameters": [ + "Object", + "-", + "round(1024 * Object.Variable(__ShakeObject_DisplacementAngle) * Object.Variable(__ShakeObject_PercentTimeElapsedThisFrame)) / 1024" + ] + } + ], + "events": [ + { + "type": "BuiltinCommonInstructions::Comment", + "color": { + "b": 109, + "g": 230, + "r": 255, + "textB": 0, + "textG": 0, + "textR": 0 + }, + "comment": "Save movement to calculate drift" + }, + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [], + "actions": [ + { + "type": { + "value": "ModVarObjet" + }, + "parameters": [ + "Object", + "__ShakeObject_AngleTravelled", + "-", + "round(1024 * Object.Variable(__ShakeObject_DisplacementAngle) * Object.Variable(__ShakeObject_PercentTimeElapsedThisFrame)) / 1024" + ] + } + ] + } + ] + } + ] + } + ], + "parameters": [] + } + ] + } + ], + "parameters": [] + }, + { + "colorB": 228, + "colorG": 176, + "colorR": 74, + "creationTime": 0, + "name": "Stop shaking", + "source": "", + "type": "BuiltinCommonInstructions::Group", + "events": [ + { + "type": "BuiltinCommonInstructions::Comment", + "color": { + "b": 109, + "g": 230, + "r": 255, + "textB": 0, + "textG": 0, + "textR": 0 + }, + "comment": "Stop shaking when the duration has been reached (or if the stop shaking function was called)" + }, + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [ + { + "type": { + "value": "BuiltinCommonInstructions::Or" + }, + "parameters": [], + "subInstructions": [ + { + "type": { + "value": "ObjectTimer" + }, + "parameters": [ + "Object", + "\"__ShakeObject_DurationTimer\"", + "Object.Variable(__ShakeObject_Duration)" + ] + }, + { + "type": { + "value": "VarObjet" + }, + "parameters": [ + "Object", + "__ShakeObject_ShakeInProgress", + "=", + "-1" + ] + } + ] + }, + { + "type": { + "value": "BuiltinCommonInstructions::Once" + }, + "parameters": [] + } + ], + "actions": [ + { + "type": { + "value": "ModVarObjet" + }, + "parameters": [ + "Object", + "__ShakeObject_ShakeInProgress", + "=", + "0" + ] + }, + { + "type": { + "value": "ModVarObjet" + }, + "parameters": [ + "Object", + "__ShakeObject_ShakeCounter", + "=", + "0" + ] + } + ], + "events": [ + { + "colorB": 228, + "colorG": 176, + "colorR": 74, + "creationTime": 0, + "name": "Correct for drift and reset drift tracking variables", + "source": "", + "type": "BuiltinCommonInstructions::Group", + "events": [ + { + "type": "BuiltinCommonInstructions::Comment", + "color": { + "b": 109, + "g": 230, + "r": 255, + "textB": 0, + "textG": 0, + "textR": 0 + }, + "comment": "Move to correct any drift from previous shake" + }, + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [ + { + "type": { + "value": "BuiltinCommonInstructions::Or" + }, + "parameters": [], + "subInstructions": [ + { + "type": { + "value": "VarObjet" + }, + "parameters": [ + "Object", + "__ShakeObject_PowerX", + "!=", + "0" + ] + }, + { + "type": { + "value": "VarObjet" + }, + "parameters": [ + "Object", + "__ShakeObject_PowerY", + "!=", + "0" + ] + } + ] + } + ], + "actions": [ + { + "type": { + "value": "SetX" + }, + "parameters": [ + "Object", + "-", + "Object.Variable(__ShakeObject_DisplacementTravelledX)" + ] + }, + { + "type": { + "value": "SetY" + }, + "parameters": [ + "Object", + "-", + "Object.Variable(__ShakeObject_DisplacementTravelledY)" + ] + } + ] + }, + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [ + { + "type": { + "value": "VarObjet" + }, + "parameters": [ + "Object", + "__ShakeObject_PowerAngle", + "!=", + "0" + ] + } + ], + "actions": [ + { + "type": { + "value": "SetAngle" + }, + "parameters": [ + "Object", + "-", + "Object.Variable(__ShakeObject_AngleTravelled)" + ] + } + ] + }, + { + "type": "BuiltinCommonInstructions::Comment", + "color": { + "b": 109, + "g": 230, + "r": 255, + "textB": 0, + "textG": 0, + "textR": 0 + }, + "comment": "Reset drift detection variables" + }, + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [], + "actions": [ + { + "type": { + "value": "ModVarObjet" + }, + "parameters": [ + "Object", + "__ShakeObject_DisplacementTravelledX", + "=", + "0" + ] + }, + { + "type": { + "value": "ModVarObjet" + }, + "parameters": [ + "Object", + "__ShakeObject_DisplacementTravelledY", + "=", + "0" + ] + }, + { + "type": { + "value": "ModVarObjet" + }, + "parameters": [ + "Object", + "__ShakeObject_AngleTravelled", + "=", + "0" + ] + } + ] + } + ], + "parameters": [] + } + ] + } + ], + "parameters": [] + } + ], + "parameters": [] + } + ], + "parameters": [ + { + "description": "Object", + "name": "Object", + "type": "object" + }, + { + "description": "Behavior", + "name": "Behavior", + "supplementaryInformation": "ShakeObject::ShakeObject_PositionAngle", + "type": "behavior" + } + ], + "objectGroups": [] + }, + { + "description": "Stop any shaking of object that was initiated by the Shake Object extension.", + "fullName": "Stop shaking the object", + "functionType": "Action", + "name": "StopShaking", + "sentence": "Stop shaking _PARAM0_", + "events": [ + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [ + { + "type": { + "value": "ShakeObject::ShakeObject_PositionAngle::IsShaking" + }, + "parameters": [ + "Object", + "Behavior", + "" + ] + } + ], + "actions": [ + { + "type": { + "value": "ModVarObjet" + }, + "parameters": [ + "Object", + "__ShakeObject_ShakeInProgress", + "=", + "-1" + ] + } + ] + } + ], + "parameters": [ + { + "description": "Object", + "name": "Object", + "type": "object" + }, + { + "description": "Behavior", + "name": "Behavior", + "supplementaryInformation": "ShakeObject::ShakeObject_PositionAngle", + "type": "behavior" + } + ], + "objectGroups": [] + }, + { + "description": "Check if an object is shaking.", + "fullName": "Check if an object is shaking", + "functionType": "Condition", + "name": "IsShaking", + "sentence": "_PARAM0_ is shaking", + "events": [ + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [ + { + "type": { + "value": "VarObjet" + }, + "parameters": [ + "Object", + "__ShakeObject_ShakeInProgress", + "=", + "1" + ] + } + ], + "actions": [ + { + "type": { + "value": "SetReturnBoolean" + }, + "parameters": [ + "True" + ] + } + ] + } + ], + "parameters": [ + { + "description": "Object", + "name": "Object", + "type": "object" + }, + { + "description": "Behavior", + "name": "Behavior", + "supplementaryInformation": "ShakeObject::ShakeObject_PositionAngle", + "type": "behavior" + } + ], + "objectGroups": [] + } + ], + "eventsFunctionsFolderStructure": { + "folderName": "__ROOT", + "children": [ + { + "functionName": "ShakeObject_PositionAngle" + }, + { + "functionName": "doStepPostEvents" + }, + { + "functionName": "StopShaking" + }, + { + "functionName": "IsShaking" + } + ] + }, + "propertyDescriptors": [], + "propertiesFolderStructure": { + "folderName": "__ROOT" + } + }, + { + "description": "Shake objects with translation, rotation and scale.", + "fullName": "Shake object (position, angle, scale)", + "helpPath": "", + "iconUrl": "", + "name": "ShakeObject_PositionAngleScale", + "objectType": "", + "previewIconUrl": "", + "eventsFunctions": [ + { + "description": "Shake an object, using one or more ways to shake (position, angle, scale). Make sure to \"Stop shaking\" before starting a new shake if it uses different parameters.", + "fullName": "Shake object (position, angle, scale)", + "functionType": "Action", + "name": "ShakeObject_PositionAngleScale", + "sentence": "Shake object _PARAM0_ for _PARAM2_ seconds. Modify position amplitude _PARAM3_ on X axis and _PARAM4_ on Y axis, angle rotation amplitude _PARAM5_, and scale amplitude _PARAM6_. Wait _PARAM7_ seconds between shakes. Keep shaking until stopped: _PARAM8_", + "events": [ + { + "colorB": 228, + "colorG": 176, + "colorR": 74, + "creationTime": 0, + "name": "Shake Object", + "source": "", + "type": "BuiltinCommonInstructions::Group", + "events": [ + { + "type": "BuiltinCommonInstructions::Comment", + "color": { + "b": 109, + "g": 230, + "r": 255, + "textB": 0, + "textG": 0, + "textR": 0 + }, + "comment": "Start/Reset duration timer" + }, + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [], + "actions": [ + { + "type": { + "value": "ResetObjectTimer" + }, + "parameters": [ + "Object", + "\"__ShakeObject_DurationTimer\"" + ] + } + ] + }, + { + "type": "BuiltinCommonInstructions::Comment", + "color": { + "b": 109, + "g": 230, + "r": 255, + "textB": 0, + "textG": 0, + "textR": 0 + }, + "comment": "Pass input parameters to global variables so that onScenePostEvents can use them" + }, + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [], + "actions": [ + { + "type": { + "value": "ModVarObjet" + }, + "parameters": [ + "Object", + "__ShakeObject_Duration", + "=", + "Duration" + ] + }, + { + "type": { + "value": "ModVarObjet" + }, + "parameters": [ + "Object", + "__ShakeObject_PowerX", + "=", + "PowerX" + ] + }, + { + "type": { + "value": "ModVarObjet" + }, + "parameters": [ + "Object", + "__ShakeObject_PowerY", + "=", + "PowerY" + ] + }, + { + "type": { + "value": "ModVarObjet" + }, + "parameters": [ + "Object", + "__ShakeObject_PowerAngle", + "=", + "PowerAngle" + ] + }, + { + "type": { + "value": "ModVarObjet" + }, + "parameters": [ + "Object", + "__ShakeObject_PowerScale", + "=", + "PowerScale" + ] + }, + { + "type": { + "value": "ModVarObjet" + }, + "parameters": [ + "Object", + "__ShakeObject_TimeBetweenShakes", + "=", + "TimeBetweenShakes" + ] + } + ] + }, + { + "type": "BuiltinCommonInstructions::Comment", + "color": { + "b": 109, + "g": 230, + "r": 255, + "textB": 0, + "textG": 0, + "textR": 0 + }, + "comment": "Determine if the shake should keep going until stopped" + }, + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [], + "actions": [ + { + "type": { + "value": "SetObjectVariableAsBoolean" + }, + "parameters": [ + "Object", + "__ShakeObject_ShakeForever", + "False" + ] + } + ] + }, + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [ + { + "type": { + "value": "BooleanVariable" + }, + "parameters": [ + "ShakeForever", + "True", + "" + ] + } + ], + "actions": [ + { + "type": { + "value": "SetObjectVariableAsBoolean" + }, + "parameters": [ + "Object", + "__ShakeObject_ShakeForever", + "True" + ] + } + ] + }, + { + "type": "BuiltinCommonInstructions::Comment", + "color": { + "b": 109, + "g": 230, + "r": 255, + "textB": 0, + "textG": 0, + "textR": 0 + }, + "comment": "Add default values if none were provided" + }, + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [ + { + "type": { + "value": "VarObjet" + }, + "parameters": [ + "Object", + "__ShakeObject_Duration", + "=", + "0" + ] + } + ], + "actions": [ + { + "type": { + "value": "ModVarObjet" + }, + "parameters": [ + "Object", + "__ShakeObject_Duration", + "=", + "0.5" + ] + } + ] + }, + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [ + { + "type": { + "value": "VarObjet" + }, + "parameters": [ + "Object", + "__ShakeObject_TimeBetweenShakes", + "=", + "0" + ] + } + ], + "actions": [ + { + "type": { + "value": "ModVarObjet" + }, + "parameters": [ + "Object", + "__ShakeObject_TimeBetweenShakes", + "=", + "0.08" + ] + } + ] + }, + { + "type": "BuiltinCommonInstructions::Comment", + "color": { + "b": 109, + "g": 230, + "r": 255, + "textB": 0, + "textG": 0, + "textR": 0 + }, + "comment": "If duration is less than a single shake, increase duration to make 1 full shake" + }, + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [ + { + "type": { + "value": "VarObjet" + }, + "parameters": [ + "Object", + "__ShakeObject_Duration", + "<", + "Object.Variable(__ShakeObject_TimeBetweenShakes)" + ] + } + ], + "actions": [ + { + "type": { + "value": "ModVarObjet" + }, + "parameters": [ + "Object", + "__ShakeObject_Duration", + "=", + "Object.Variable(__ShakeObject_TimeBetweenShakes)" + ] + } + ] + }, + { + "type": "BuiltinCommonInstructions::Comment", + "color": { + "b": 109, + "g": 230, + "r": 255, + "textB": 0, + "textG": 0, + "textR": 0 + }, + "comment": "Detect initial shake" + }, + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [ + { + "type": { + "value": "VarObjet" + }, + "parameters": [ + "Object", + "__ShakeObject_ShakeInProgress", + "=", + "0" + ] + } + ], + "actions": [ + { + "type": { + "value": "ModVarObjet" + }, + "parameters": [ + "Object", + "__ShakeObject_InitialShake", + "=", + "1" + ] + } + ] + }, + { + "type": "BuiltinCommonInstructions::Comment", + "color": { + "b": 109, + "g": 230, + "r": 255, + "textB": 0, + "textG": 0, + "textR": 0 + }, + "comment": "Initiate the onScenePostEvents function" + }, + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [], + "actions": [ + { + "type": { + "value": "ModVarObjet" + }, + "parameters": [ + "Object", + "__ShakeObject_ShakeInProgress", + "=", + "1" + ] + } + ] + } + ], + "parameters": [] + } + ], + "parameters": [ + { + "description": "Object", + "name": "Object", + "type": "object" + }, + { + "description": "Behavior", + "name": "Behavior", + "supplementaryInformation": "ShakeObject::ShakeObject_PositionAngleScale", + "type": "behavior" + }, + { + "description": "Duration of shake (in seconds) (Default: 0.5)", + "name": "Duration", + "type": "expression" + }, + { + "description": "Amplitude of postion shake in X direction (in pixels) (For example: 5)", + "name": "PowerX", + "type": "expression" + }, + { + "description": "Amplitude of position shake in Y direction (in pixels) (For example: 5)", + "longDescription": "Use a negative number to make the single-shake move in the opposite direction.", + "name": "PowerY", + "type": "expression" + }, + { + "description": "Amplitude of angle rotation shake (in degrees) (For example: 5)", + "name": "PowerAngle", + "type": "expression" + }, + { + "description": "Amplitude of scale shake (in percent change) (For example: 5)", + "name": "PowerScale", + "type": "expression" + }, + { + "description": "Amount of time between shakes (in seconds) (Default: 0.08)", + "longDescription": "For a single-shake effect, set it to the same value as \"Duration\".", + "name": "TimeBetweenShakes", + "type": "expression" + }, + { + "description": "Keep shaking until stopped", + "longDescription": "Duration value will be ignored", + "name": "ShakeForever", + "type": "yesorno" + } + ], + "objectGroups": [] + }, + { + "fullName": "", + "functionType": "Action", + "name": "doStepPostEvents", + "sentence": "", + "events": [ + { + "colorB": 228, + "colorG": 176, + "colorR": 74, + "creationTime": 0, + "name": "Shake Object", + "source": "", + "type": "BuiltinCommonInstructions::Group", + "events": [ + { + "colorB": 228, + "colorG": 176, + "colorR": 74, + "creationTime": 0, + "name": "Start shaking", + "source": "", + "type": "BuiltinCommonInstructions::Group", + "events": [ + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [ + { + "type": { + "value": "VarObjet" + }, + "parameters": [ + "Object", + "__ShakeObject_ShakeInProgress", + "=", + "1" + ] + } + ], + "actions": [], + "events": [ + { + "type": "BuiltinCommonInstructions::Comment", + "color": { + "b": 109, + "g": 230, + "r": 255, + "textB": 0, + "textG": 0, + "textR": 0 + }, + "comment": "Keep object shaking forever (if desired)" + }, + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [ + { + "type": { + "value": "ObjectVariableAsBoolean" + }, + "parameters": [ + "Object", + "__ShakeObject_ShakeForever", + "True" + ] + } + ], + "actions": [ + { + "type": { + "value": "ModVarObjet" + }, + "parameters": [ + "Object", + "__ShakeObject_Duration", + "=", + "100" + ] + }, + { + "type": { + "value": "ResetObjectTimer" + }, + "parameters": [ + "Object", + "\"__ShakeObject_DurationTimer\"" + ] + } + ] + }, + { + "colorB": 228, + "colorG": 176, + "colorR": 74, + "creationTime": 0, + "name": "Calculate movement of the shake", + "source": "", + "type": "BuiltinCommonInstructions::Group", + "events": [ + { + "type": "BuiltinCommonInstructions::Comment", + "color": { + "b": 109, + "g": 230, + "r": 255, + "textB": 0, + "textG": 0, + "textR": 0 + }, + "comment": "Run once before every shake movement" + }, + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [ + { + "type": { + "value": "BuiltinCommonInstructions::Or" + }, + "parameters": [], + "subInstructions": [ + { + "type": { + "value": "VarObjet" + }, + "parameters": [ + "Object", + "__ShakeObject_InitialShake", + "=", + "1" + ] + }, + { + "type": { + "value": "ObjectTimer" + }, + "parameters": [ + "Object", + "\"__ShakeObject_ShakeTimer\"", + "Object.Variable(__ShakeObject_TimeBetweenShakes)" + ] + } + ] + } + ], + "actions": [ + { + "type": { + "value": "ResetObjectTimer" + }, + "parameters": [ + "Object", + "\"__ShakeObject_ShakeTimer\"" + ] + }, + { + "type": { + "value": "ModVarObjet" + }, + "parameters": [ + "Object", + "__ShakeObject_ShakeCounter", + "+", + "1" + ] + } + ], + "events": [ + { + "colorB": 228, + "colorG": 176, + "colorR": 74, + "creationTime": 0, + "folded": true, + "name": "Correct for drift and reset drift tracking variables", + "source": "", + "type": "BuiltinCommonInstructions::Group", + "events": [ + { + "type": "BuiltinCommonInstructions::Comment", + "color": { + "b": 109, + "g": 230, + "r": 255, + "textB": 0, + "textG": 0, + "textR": 0 + }, + "comment": "Move to correct any drift from previous shake" + }, + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [ + { + "type": { + "value": "BuiltinCommonInstructions::Or" + }, + "parameters": [], + "subInstructions": [ + { + "type": { + "value": "VarObjet" + }, + "parameters": [ + "Object", + "__ShakeObject_PowerX", + "!=", + "0" + ] + }, + { + "type": { + "value": "VarObjet" + }, + "parameters": [ + "Object", + "__ShakeObject_PowerY", + "!=", + "0" + ] + } + ] + } + ], + "actions": [ + { + "type": { + "value": "SetX" + }, + "parameters": [ + "Object", + "-", + "Object.Variable(__ShakeObject_DisplacementTravelledX)" + ] + }, + { + "type": { + "value": "SetY" + }, + "parameters": [ + "Object", + "-", + "Object.Variable(__ShakeObject_DisplacementTravelledY)" + ] + } + ] + }, + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [ + { + "type": { + "value": "VarObjet" + }, + "parameters": [ + "Object", + "__ShakeObject_PowerAngle", + "!=", + "0" + ] + } + ], + "actions": [ + { + "type": { + "value": "SetAngle" + }, + "parameters": [ + "Object", + "-", + "Object.Variable(__ShakeObject_AngleTravelled)" + ] + } + ] + }, + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [ + { + "type": { + "value": "VarObjet" + }, + "parameters": [ + "Object", + "__ShakeObject_PowerScale", + "!=", + "0" + ] + } + ], + "actions": [ + { + "type": { + "value": "ScalableCapability::ScalableBehavior::SetValue" + }, + "parameters": [ + "Object", + "Scale", + "-", + "Object.Variable(__ShakeObject_ScaleTravelled)" + ] + } + ] + }, + { + "type": "BuiltinCommonInstructions::Comment", + "color": { + "b": 109, + "g": 230, + "r": 255, + "textB": 0, + "textG": 0, + "textR": 0 + }, + "comment": "Reset drift detection variables" + }, + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [], + "actions": [ + { + "type": { + "value": "ModVarObjet" + }, + "parameters": [ + "Object", + "__ShakeObject_DisplacementTravelledX", + "=", + "0" + ] + }, + { + "type": { + "value": "ModVarObjet" + }, + "parameters": [ + "Object", + "__ShakeObject_DisplacementTravelledY", + "=", + "0" + ] + }, + { + "type": { + "value": "ModVarObjet" + }, + "parameters": [ + "Object", + "__ShakeObject_AngleTravelled", + "=", + "0" + ] + }, + { + "type": { + "value": "ModVarObjet" + }, + "parameters": [ + "Object", + "__ShakeObject_ScaleTravelled", + "=", + "0" + ] + } + ] + } + ], + "parameters": [] + }, + { + "type": "BuiltinCommonInstructions::Comment", + "color": { + "b": 109, + "g": 230, + "r": 255, + "textB": 0, + "textG": 0, + "textR": 0 + }, + "comment": "Calculate Position Shake" + }, + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [ + { + "type": { + "value": "BuiltinCommonInstructions::Or" + }, + "parameters": [], + "subInstructions": [ + { + "type": { + "value": "VarObjet" + }, + "parameters": [ + "Object", + "__ShakeObject_PowerX", + "!=", + "0" + ] + }, + { + "type": { + "value": "VarObjet" + }, + "parameters": [ + "Object", + "__ShakeObject_PowerY", + "!=", + "0" + ] + } + ] + } + ], + "actions": [], + "events": [ + { + "type": "BuiltinCommonInstructions::Comment", + "color": { + "b": 109, + "g": 230, + "r": 255, + "textB": 0, + "textG": 0, + "textR": 0 + }, + "comment": "PositionDisplacement = (DesiredDuration - RunningTimer) / DesiredDuration * Amplitude * [-1 or 1]" + }, + { + "type": "BuiltinCommonInstructions::Comment", + "color": { + "b": 109, + "g": 230, + "r": 255, + "textB": 0, + "textG": 0, + "textR": 0 + }, + "comment": "Make initial shake NOT random so users can set a direction for a one-shake effect" + }, + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [ + { + "type": { + "value": "VarObjet" + }, + "parameters": [ + "Object", + "__ShakeObject_InitialShake", + "=", + "1" + ] + } + ], + "actions": [], + "events": [ + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [ + { + "type": { + "value": "VarObjet" + }, + "parameters": [ + "Object", + "__ShakeObject_PowerX", + "!=", + "0" + ] + } + ], + "actions": [ + { + "type": { + "value": "ModVarObjet" + }, + "parameters": [ + "Object", + "__ShakeObject_DisplacementX", + "=", + "(Object.Variable(__ShakeObject_Duration) - Object.ObjectTimerElapsedTime(\"__ShakeObject_DurationTimer\")) / Object.Variable(__ShakeObject_Duration) * Object.Variable(__ShakeObject_PowerX)" + ] + } + ] + }, + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [ + { + "type": { + "value": "VarObjet" + }, + "parameters": [ + "Object", + "__ShakeObject_PowerY", + "!=", + "0" + ] + } + ], + "actions": [ + { + "type": { + "value": "ModVarObjet" + }, + "parameters": [ + "Object", + "__ShakeObject_DisplacementY", + "=", + "(Object.Variable(__ShakeObject_Duration) - Object.ObjectTimerElapsedTime(\"__ShakeObject_DurationTimer\")) / Object.Variable(__ShakeObject_Duration) * Object.Variable(__ShakeObject_PowerY)" + ] + } + ] + } + ] + }, + { + "type": "BuiltinCommonInstructions::Comment", + "color": { + "b": 109, + "g": 230, + "r": 255, + "textB": 0, + "textG": 0, + "textR": 0 + }, + "comment": "After initial shake pick a random direction" + }, + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [ + { + "type": { + "value": "VarObjet" + }, + "parameters": [ + "Object", + "__ShakeObject_InitialShake", + "=", + "0" + ] + } + ], + "actions": [], + "events": [ + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [ + { + "type": { + "value": "VarObjet" + }, + "parameters": [ + "Object", + "__ShakeObject_PowerX", + "!=", + "0" + ] + } + ], + "actions": [ + { + "type": { + "value": "ModVarObjet" + }, + "parameters": [ + "Object", + "__ShakeObject_DisplacementX", + "=", + "(Object.Variable(__ShakeObject_Duration) - Object.ObjectTimerElapsedTime(\"__ShakeObject_DurationTimer\")) / Object.Variable(__ShakeObject_Duration) * Object.Variable(__ShakeObject_PowerX) * RandomWithStep(-1, 1, 2)" + ] + } + ] + }, + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [ + { + "type": { + "value": "VarObjet" + }, + "parameters": [ + "Object", + "__ShakeObject_PowerY", + "!=", + "0" + ] + } + ], + "actions": [ + { + "type": { + "value": "ModVarObjet" + }, + "parameters": [ + "Object", + "__ShakeObject_DisplacementY", + "=", + "(Object.Variable(__ShakeObject_Duration) - Object.ObjectTimerElapsedTime(\"__ShakeObject_DurationTimer\")) / Object.Variable(__ShakeObject_Duration) * Object.Variable(__ShakeObject_PowerY) * RandomWithStep(-1, 1, 2)" + ] + } + ] + } + ] + } + ] + }, + { + "type": "BuiltinCommonInstructions::Comment", + "color": { + "b": 109, + "g": 230, + "r": 255, + "textB": 0, + "textG": 0, + "textR": 0 + }, + "comment": "Calculate Rotation (angle) shake " + }, + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [ + { + "type": { + "value": "VarObjet" + }, + "parameters": [ + "Object", + "__ShakeObject_PowerAngle", + "!=", + "0" + ] + } + ], + "actions": [], + "events": [ + { + "type": "BuiltinCommonInstructions::Comment", + "color": { + "b": 109, + "g": 230, + "r": 255, + "textB": 0, + "textG": 0, + "textR": 0 + }, + "comment": "Every \"even\" shake, rotate clockwise" + }, + { + "type": "BuiltinCommonInstructions::Comment", + "color": { + "b": 109, + "g": 230, + "r": 255, + "textB": 0, + "textG": 0, + "textR": 0 + }, + "comment": "AngleDisplacement = (DesiredDuration - RunningTimer) / DesiredDuration * Amplitude" + }, + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [ + { + "type": { + "value": "BuiltinCommonInstructions::CompareNumbers" + }, + "parameters": [ + "mod(Object.Variable(__ShakeObject_ShakeCounter),2)", + "=", + "0" + ] + } + ], + "actions": [ + { + "type": { + "value": "ModVarObjet" + }, + "parameters": [ + "Object", + "__ShakeObject_DisplacementAngle", + "=", + "-1 * (Object.Variable(__ShakeObject_Duration) - Object.ObjectTimerElapsedTime(\"__ShakeObject_DurationTimer\")) / Object.Variable(__ShakeObject_Duration) * Object.Variable(__ShakeObject_PowerAngle)" + ] + } + ] + }, + { + "type": "BuiltinCommonInstructions::Comment", + "color": { + "b": 109, + "g": 230, + "r": 255, + "textB": 0, + "textG": 0, + "textR": 0 + }, + "comment": "Every \"odd\" shake, rotate counter-clockwise" + }, + { + "type": "BuiltinCommonInstructions::Comment", + "color": { + "b": 109, + "g": 230, + "r": 255, + "textB": 0, + "textG": 0, + "textR": 0 + }, + "comment": "AngleDisplacement = (DesiredDuration - RunningTimer) / DesiredDuration * Amplitude" + }, + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [ + { + "type": { + "value": "BuiltinCommonInstructions::CompareNumbers" + }, + "parameters": [ + "mod(Object.Variable(__ShakeObject_ShakeCounter),2)", + "=", + "1" + ] + } + ], + "actions": [ + { + "type": { + "value": "ModVarObjet" + }, + "parameters": [ + "Object", + "__ShakeObject_DisplacementAngle", + "=", + "(Object.Variable(__ShakeObject_Duration) - Object.ObjectTimerElapsedTime(\"__ShakeObject_DurationTimer\")) / Object.Variable(__ShakeObject_Duration) * Object.Variable(__ShakeObject_PowerAngle)" + ] + } + ] + } + ] + }, + { + "type": "BuiltinCommonInstructions::Comment", + "color": { + "b": 109, + "g": 230, + "r": 255, + "textB": 0, + "textG": 0, + "textR": 0 + }, + "comment": "Calculate Scale shake" + }, + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [ + { + "type": { + "value": "VarObjet" + }, + "parameters": [ + "Object", + "__ShakeObject_PowerScale", + "!=", + "0" + ] + } + ], + "actions": [], + "events": [ + { + "type": "BuiltinCommonInstructions::Comment", + "color": { + "b": 109, + "g": 230, + "r": 255, + "textB": 0, + "textG": 0, + "textR": 0 + }, + "comment": "Calculate object scale displacement, with linear decay over time" + }, + { + "type": "BuiltinCommonInstructions::Comment", + "color": { + "b": 109, + "g": 230, + "r": 255, + "textB": 0, + "textG": 0, + "textR": 0 + }, + "comment": "ScaleDisplacement = (DesiredDuration - RunningTimer) / DesiredDuration * Amplitude * 1/100" + }, + { + "type": "BuiltinCommonInstructions::Comment", + "color": { + "b": 109, + "g": 230, + "r": 255, + "textB": 0, + "textG": 0, + "textR": 0 + }, + "comment": "Every even shake, increase scale" + }, + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [ + { + "type": { + "value": "BuiltinCommonInstructions::CompareNumbers" + }, + "parameters": [ + "mod(Object.Variable(__ShakeObject_ShakeCounter),2)", + "=", + "0" + ] + } + ], + "actions": [ + { + "type": { + "value": "ModVarObjet" + }, + "parameters": [ + "Object", + "__ShakeObject_DisplacementScale", + "=", + "-1 * (Object.Variable(__ShakeObject_Duration) - Object.ObjectTimerElapsedTime(\"__ShakeObject_DurationTimer\")) / Object.Variable(__ShakeObject_Duration) * Object.Variable(__ShakeObject_PowerScale) * (1/100)" + ] + } + ] + }, + { + "type": "BuiltinCommonInstructions::Comment", + "color": { + "b": 109, + "g": 230, + "r": 255, + "textB": 0, + "textG": 0, + "textR": 0 + }, + "comment": "Every \"odd\" shake, decrease scale" + }, + { + "type": "BuiltinCommonInstructions::Comment", + "color": { + "b": 109, + "g": 230, + "r": 255, + "textB": 0, + "textG": 0, + "textR": 0 + }, + "comment": "ScaleDisplacement = -1 * (DesiredDuration - RunningTimer) / DesiredDuration * Amplitude * 1/100" + }, + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [ + { + "type": { + "value": "BuiltinCommonInstructions::CompareNumbers" + }, + "parameters": [ + "mod(Object.Variable(__ShakeObject_ShakeCounter),2)", + "=", + "1" + ] + } + ], + "actions": [ + { + "type": { + "value": "ModVarObjet" + }, + "parameters": [ + "Object", + "__ShakeObject_DisplacementScale", + "=", + "(Object.Variable(__ShakeObject_Duration) - Object.ObjectTimerElapsedTime(\"__ShakeObject_DurationTimer\")) / Object.Variable(__ShakeObject_Duration) * Object.Variable(__ShakeObject_PowerScale) * (1/100)" + ] + } + ] + } + ] + }, + { + "type": "BuiltinCommonInstructions::Comment", + "color": { + "b": 109, + "g": 230, + "r": 255, + "textB": 0, + "textG": 0, + "textR": 0 + }, + "comment": "Save that initial shake has been processed" + }, + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [], + "actions": [ + { + "type": { + "value": "ModVarObjet" + }, + "parameters": [ + "Object", + "__ShakeObject_InitialShake", + "=", + "0" + ] + } + ] + } + ] + } + ], + "parameters": [] + }, + { + "colorB": 228, + "colorG": 176, + "colorR": 74, + "creationTime": 0, + "name": "Move object", + "source": "", + "type": "BuiltinCommonInstructions::Group", + "events": [ + { + "type": "BuiltinCommonInstructions::Comment", + "color": { + "b": 109, + "g": 230, + "r": 255, + "textB": 0, + "textG": 0, + "textR": 0 + }, + "comment": "Calculate the fraction of shake that occured during this frame" + }, + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [], + "actions": [ + { + "type": { + "value": "ModVarObjet" + }, + "parameters": [ + "Object", + "__ShakeObject_PercentTimeElapsedThisFrame", + "=", + "min(1,TimeDelta()/Object.Variable(__ShakeObject_TimeBetweenShakes))" + ] + } + ] + }, + { + "type": "BuiltinCommonInstructions::Comment", + "color": { + "b": 109, + "g": 230, + "r": 255, + "textB": 0, + "textG": 0, + "textR": 0 + }, + "comment": "Increase change for the first half of the shake (move away from original values)" + }, + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [ + { + "type": { + "inverted": true, + "value": "ObjectTimer" + }, + "parameters": [ + "Object", + "\"__ShakeObject_ShakeTimer\"", + "Object.Variable(__ShakeObject_TimeBetweenShakes)/2" + ] + } + ], + "actions": [], + "events": [ + { + "type": "BuiltinCommonInstructions::Comment", + "color": { + "b": 109, + "g": 230, + "r": 255, + "textB": 0, + "textG": 0, + "textR": 0 + }, + "comment": "Change position" + }, + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [ + { + "type": { + "value": "BuiltinCommonInstructions::Or" + }, + "parameters": [], + "subInstructions": [ + { + "type": { + "value": "VarObjet" + }, + "parameters": [ + "Object", + "__ShakeObject_PowerX", + "!=", + "0" + ] + }, + { + "type": { + "value": "VarObjet" + }, + "parameters": [ + "Object", + "__ShakeObject_PowerY", + "!=", + "0" + ] + } + ] + } + ], + "actions": [ + { + "type": { + "value": "SetX" + }, + "parameters": [ + "Object", + "+", + "round(1024 * Object.Variable(__ShakeObject_DisplacementX) * Object.Variable(__ShakeObject_PercentTimeElapsedThisFrame)) / 1024" + ] + }, + { + "type": { + "value": "SetY" + }, + "parameters": [ + "Object", + "+", + "round(1024 * Object.Variable(__ShakeObject_DisplacementY) * Object.Variable(__ShakeObject_PercentTimeElapsedThisFrame)) / 1024" + ] + } + ], + "events": [ + { + "type": "BuiltinCommonInstructions::Comment", + "color": { + "b": 109, + "g": 230, + "r": 255, + "textB": 0, + "textG": 0, + "textR": 0 + }, + "comment": "Save movement to calculate drift" + }, + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [], + "actions": [ + { + "type": { + "value": "ModVarObjet" + }, + "parameters": [ + "Object", + "__ShakeObject_DisplacementTravelledX", + "+", + "round(1024 * Object.Variable(__ShakeObject_DisplacementX) * Object.Variable(__ShakeObject_PercentTimeElapsedThisFrame)) / 1024" + ] + }, + { + "type": { + "value": "ModVarObjet" + }, + "parameters": [ + "Object", + "__ShakeObject_DisplacementTravelledY", + "+", + "round(1024 * Object.Variable(__ShakeObject_DisplacementY) * Object.Variable(__ShakeObject_PercentTimeElapsedThisFrame)) / 1024" + ] + } + ] + } + ] + }, + { + "type": "BuiltinCommonInstructions::Comment", + "color": { + "b": 109, + "g": 230, + "r": 255, + "textB": 0, + "textG": 0, + "textR": 0 + }, + "comment": "Change angle" + }, + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [ + { + "type": { + "value": "BuiltinCommonInstructions::CompareNumbers" + }, + "parameters": [ + "Object.Variable(__ShakeObject_PowerAngle)", + "!=", + "0" + ] + } + ], + "actions": [ + { + "type": { + "value": "SetAngle" + }, + "parameters": [ + "Object", + "+", + "round(1024 * Object.Variable(__ShakeObject_DisplacementAngle) * Object.Variable(__ShakeObject_PercentTimeElapsedThisFrame)) / 1024" + ] + } + ], + "events": [ + { + "type": "BuiltinCommonInstructions::Comment", + "color": { + "b": 109, + "g": 230, + "r": 255, + "textB": 0, + "textG": 0, + "textR": 0 + }, + "comment": "Save movement to calculate drift" + }, + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [], + "actions": [ + { + "type": { + "value": "ModVarObjet" + }, + "parameters": [ + "Object", + "__ShakeObject_AngleTravelled", + "+", + "round(1024 * Object.Variable(__ShakeObject_DisplacementAngle) * Object.Variable(__ShakeObject_PercentTimeElapsedThisFrame)) / 1024" + ] + } + ] + } + ] + }, + { + "type": "BuiltinCommonInstructions::Comment", + "color": { + "b": 109, + "g": 230, + "r": 255, + "textB": 0, + "textG": 0, + "textR": 0 + }, + "comment": "Change scale" + }, + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [ + { + "type": { + "value": "BuiltinCommonInstructions::CompareNumbers" + }, + "parameters": [ + "Object.Variable(__ShakeObject_PowerScale)", + "!=", + "0" + ] + } + ], + "actions": [ + { + "type": { + "value": "ScalableCapability::ScalableBehavior::SetValue" + }, + "parameters": [ + "Object", + "Scale", + "+", + "round(1024 * Object.Variable(__ShakeObject_DisplacementScale) * Object.Variable(__ShakeObject_PercentTimeElapsedThisFrame)) / 1024" + ] + } + ], + "events": [ + { + "type": "BuiltinCommonInstructions::Comment", + "color": { + "b": 109, + "g": 230, + "r": 255, + "textB": 0, + "textG": 0, + "textR": 0 + }, + "comment": "Save movement to calculate drift" + }, + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [], + "actions": [ + { + "type": { + "value": "ModVarObjet" + }, + "parameters": [ + "Object", + "__ShakeObject_ScaleTravelled", + "+", + "round(1024 * Object.Variable(__ShakeObject_DisplacementScale) * Object.Variable(__ShakeObject_PercentTimeElapsedThisFrame)) / 1024" + ] + } + ] + } + ] + } + ] + }, + { + "type": "BuiltinCommonInstructions::Comment", + "color": { + "b": 109, + "g": 230, + "r": 255, + "textB": 0, + "textG": 0, + "textR": 0 + }, + "comment": "Decrease change the second half of the shake (return to original position)" + }, + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [ + { + "type": { + "value": "ObjectTimer" + }, + "parameters": [ + "Object", + "\"__ShakeObject_ShakeTimer\"", + "Object.Variable(__ShakeObject_TimeBetweenShakes)/2" + ] + } + ], + "actions": [], + "events": [ + { + "type": "BuiltinCommonInstructions::Comment", + "color": { + "b": 109, + "g": 230, + "r": 255, + "textB": 0, + "textG": 0, + "textR": 0 + }, + "comment": "Change position" + }, + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [ + { + "type": { + "value": "BuiltinCommonInstructions::Or" + }, + "parameters": [], + "subInstructions": [ + { + "type": { + "value": "VarObjet" + }, + "parameters": [ + "Object", + "__ShakeObject_PowerX", + "!=", + "0" + ] + }, + { + "type": { + "value": "VarObjet" + }, + "parameters": [ + "Object", + "__ShakeObject_PowerY", + "!=", + "0" + ] + } + ] + } + ], + "actions": [ + { + "type": { + "value": "SetX" + }, + "parameters": [ + "Object", + "-", + "round(1024 * Object.Variable(__ShakeObject_DisplacementX) * Object.Variable(__ShakeObject_PercentTimeElapsedThisFrame)) / 1024" + ] + }, + { + "type": { + "value": "SetY" + }, + "parameters": [ + "Object", + "-", + "round(1024 * Object.Variable(__ShakeObject_DisplacementY) * Object.Variable(__ShakeObject_PercentTimeElapsedThisFrame)) / 1024" + ] + } + ], + "events": [ + { + "type": "BuiltinCommonInstructions::Comment", + "color": { + "b": 109, + "g": 230, + "r": 255, + "textB": 0, + "textG": 0, + "textR": 0 + }, + "comment": "Save movement to calculate drift" + }, + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [], + "actions": [ + { + "type": { + "value": "ModVarObjet" + }, + "parameters": [ + "Object", + "__ShakeObject_DisplacementTravelledX", + "-", + "round(1024 * Object.Variable(__ShakeObject_DisplacementX) * Object.Variable(__ShakeObject_PercentTimeElapsedThisFrame)) / 1024" + ] + }, + { + "type": { + "value": "ModVarObjet" + }, + "parameters": [ + "Object", + "__ShakeObject_DisplacementTravelledY", + "-", + "round(1024 * Object.Variable(__ShakeObject_DisplacementY) * Object.Variable(__ShakeObject_PercentTimeElapsedThisFrame)) / 1024" + ] + } + ] + } + ] + }, + { + "type": "BuiltinCommonInstructions::Comment", + "color": { + "b": 109, + "g": 230, + "r": 255, + "textB": 0, + "textG": 0, + "textR": 0 + }, + "comment": "Change angle" + }, + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [ + { + "type": { + "value": "BuiltinCommonInstructions::CompareNumbers" + }, + "parameters": [ + "Object.Variable(__ShakeObject_PowerAngle)", + "!=", + "0" + ] + } + ], + "actions": [ + { + "type": { + "value": "SetAngle" + }, + "parameters": [ + "Object", + "-", + "round(1024 * Object.Variable(__ShakeObject_DisplacementAngle) * Object.Variable(__ShakeObject_PercentTimeElapsedThisFrame)) / 1024" + ] + } + ], + "events": [ + { + "type": "BuiltinCommonInstructions::Comment", + "color": { + "b": 109, + "g": 230, + "r": 255, + "textB": 0, + "textG": 0, + "textR": 0 + }, + "comment": "Save movement to calculate drift" + }, + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [], + "actions": [ + { + "type": { + "value": "ModVarObjet" + }, + "parameters": [ + "Object", + "__ShakeObject_AngleTravelled", + "-", + "round(1024 * Object.Variable(__ShakeObject_DisplacementAngle) * Object.Variable(__ShakeObject_PercentTimeElapsedThisFrame)) / 1024" + ] + } + ] + } + ] + }, + { + "type": "BuiltinCommonInstructions::Comment", + "color": { + "b": 109, + "g": 230, + "r": 255, + "textB": 0, + "textG": 0, + "textR": 0 + }, + "comment": "Change scale" + }, + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [ + { + "type": { + "value": "BuiltinCommonInstructions::CompareNumbers" + }, + "parameters": [ + "Object.Variable(__ShakeObject_PowerScale)", + "!=", + "0" + ] + } + ], + "actions": [ + { + "type": { + "value": "ScalableCapability::ScalableBehavior::SetValue" + }, + "parameters": [ + "Object", + "Scale", + "-", + "round(1024 * Object.Variable(__ShakeObject_DisplacementScale) * Object.Variable(__ShakeObject_PercentTimeElapsedThisFrame)) / 1024" + ] + } + ], + "events": [ + { + "type": "BuiltinCommonInstructions::Comment", + "color": { + "b": 109, + "g": 230, + "r": 255, + "textB": 0, + "textG": 0, + "textR": 0 + }, + "comment": "Save movement to calculate drift" + }, + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [], + "actions": [ + { + "type": { + "value": "ModVarObjet" + }, + "parameters": [ + "Object", + "__ShakeObject_ScaleTravelled", + "-", + "round(1024 * Object.Variable(__ShakeObject_DisplacementScale) * Object.Variable(__ShakeObject_PercentTimeElapsedThisFrame)) / 1024" + ] + } + ] + } + ] + } + ] + } + ], + "parameters": [] + } + ] + } + ], + "parameters": [] + }, + { + "colorB": 228, + "colorG": 176, + "colorR": 74, + "creationTime": 0, + "name": "Stop shaking", + "source": "", + "type": "BuiltinCommonInstructions::Group", + "events": [ + { + "type": "BuiltinCommonInstructions::Comment", + "color": { + "b": 109, + "g": 230, + "r": 255, + "textB": 0, + "textG": 0, + "textR": 0 + }, + "comment": "Stop shaking when the duration has been reached (or if the stop shaking function was called)" + }, + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [ + { + "type": { + "value": "BuiltinCommonInstructions::Or" + }, + "parameters": [], + "subInstructions": [ + { + "type": { + "value": "ObjectTimer" + }, + "parameters": [ + "Object", + "\"__ShakeObject_DurationTimer\"", + "Object.Variable(__ShakeObject_Duration)" + ] + }, + { + "type": { + "value": "VarObjet" + }, + "parameters": [ + "Object", + "__ShakeObject_ShakeInProgress", + "=", + "-1" + ] + } + ] + }, + { + "type": { + "value": "BuiltinCommonInstructions::Once" + }, + "parameters": [] + } + ], + "actions": [ + { + "type": { + "value": "ModVarObjet" + }, + "parameters": [ + "Object", + "__ShakeObject_ShakeInProgress", + "=", + "0" + ] + }, + { + "type": { + "value": "ModVarObjet" + }, + "parameters": [ + "Object", + "__ShakeObject_ShakeCounter", + "=", + "0" + ] + } + ], + "events": [ + { + "colorB": 228, + "colorG": 176, + "colorR": 74, + "creationTime": 0, + "name": "Correct for drift and reset drift tracking variables", + "source": "", + "type": "BuiltinCommonInstructions::Group", + "events": [ + { + "type": "BuiltinCommonInstructions::Comment", + "color": { + "b": 109, + "g": 230, + "r": 255, + "textB": 0, + "textG": 0, + "textR": 0 + }, + "comment": "Move to correct any drift from previous shake" + }, + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [ + { + "type": { + "value": "BuiltinCommonInstructions::Or" + }, + "parameters": [], + "subInstructions": [ + { + "type": { + "value": "VarObjet" + }, + "parameters": [ + "Object", + "__ShakeObject_PowerX", + "!=", + "0" + ] + }, + { + "type": { + "value": "VarObjet" + }, + "parameters": [ + "Object", + "__ShakeObject_PowerY", + "!=", + "0" + ] + } + ] + } + ], + "actions": [ + { + "type": { + "value": "SetX" + }, + "parameters": [ + "Object", + "-", + "Object.Variable(__ShakeObject_DisplacementTravelledX)" + ] + }, + { + "type": { + "value": "SetY" + }, + "parameters": [ + "Object", + "-", + "Object.Variable(__ShakeObject_DisplacementTravelledY)" + ] + } + ] + }, + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [ + { + "type": { + "value": "VarObjet" + }, + "parameters": [ + "Object", + "__ShakeObject_PowerAngle", + "!=", + "0" + ] + } + ], + "actions": [ + { + "type": { + "value": "SetAngle" + }, + "parameters": [ + "Object", + "-", + "Object.Variable(__ShakeObject_AngleTravelled)" + ] + } + ] + }, + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [ + { + "type": { + "value": "VarObjet" + }, + "parameters": [ + "Object", + "__ShakeObject_PowerScale", + "!=", + "0" + ] + } + ], + "actions": [ + { + "type": { + "value": "ScalableCapability::ScalableBehavior::SetValue" + }, + "parameters": [ + "Object", + "Scale", + "-", + "Object.Variable(__ShakeObject_ScaleTravelled)" + ] + } + ] + }, + { + "type": "BuiltinCommonInstructions::Comment", + "color": { + "b": 109, + "g": 230, + "r": 255, + "textB": 0, + "textG": 0, + "textR": 0 + }, + "comment": "Reset drift detection variables" + }, + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [], + "actions": [ + { + "type": { + "value": "ModVarObjet" + }, + "parameters": [ + "Object", + "__ShakeObject_DisplacementTravelledX", + "=", + "0" + ] + }, + { + "type": { + "value": "ModVarObjet" + }, + "parameters": [ + "Object", + "__ShakeObject_DisplacementTravelledY", + "=", + "0" + ] + }, + { + "type": { + "value": "ModVarObjet" + }, + "parameters": [ + "Object", + "__ShakeObject_AngleTravelled", + "=", + "0" + ] + }, + { + "type": { + "value": "ModVarObjet" + }, + "parameters": [ + "Object", + "__ShakeObject_ScaleTravelled", + "=", + "0" + ] + } + ] + } + ], + "parameters": [] + } + ] + } + ], + "parameters": [] + } + ], + "parameters": [] + } + ], + "parameters": [ + { + "description": "Object", + "name": "Object", + "type": "object" + }, + { + "description": "Behavior", + "name": "Behavior", + "supplementaryInformation": "ShakeObject::ShakeObject_PositionAngleScale", + "type": "behavior" + } + ], + "objectGroups": [] + }, + { + "description": "Stop shaking an object.", + "fullName": "Stop shaking an object", + "functionType": "Action", + "name": "StopShaking", + "sentence": "Stop shaking _PARAM0_", + "events": [ + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [ + { + "type": { + "value": "ShakeObject::ShakeObject_PositionAngleScale::IsShaking" + }, + "parameters": [ + "Object", + "Behavior", + "" + ] + } + ], + "actions": [ + { + "type": { + "value": "ModVarObjet" + }, + "parameters": [ + "Object", + "__ShakeObject_ShakeInProgress", + "=", + "-1" + ] + } + ] + } + ], + "parameters": [ + { + "description": "Object", + "name": "Object", + "type": "object" + }, + { + "description": "Behavior", + "name": "Behavior", + "supplementaryInformation": "ShakeObject::ShakeObject_PositionAngleScale", + "type": "behavior" + } + ], + "objectGroups": [] + }, + { + "description": "Check if an object is shaking.", + "fullName": "Check if an object is shaking", + "functionType": "Condition", + "name": "IsShaking", + "sentence": "_PARAM0_ is shaking", + "events": [ + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [ + { + "type": { + "value": "VarObjet" + }, + "parameters": [ + "Object", + "__ShakeObject_ShakeInProgress", + "=", + "1" + ] + } + ], + "actions": [ + { + "type": { + "value": "SetReturnBoolean" + }, + "parameters": [ + "True" + ] + } + ] + } + ], + "parameters": [ + { + "description": "Object", + "name": "Object", + "type": "object" + }, + { + "description": "Behavior", + "name": "Behavior", + "supplementaryInformation": "ShakeObject::ShakeObject_PositionAngleScale", + "type": "behavior" + } + ], + "objectGroups": [] + } + ], + "eventsFunctionsFolderStructure": { + "folderName": "__ROOT", + "children": [ + { + "functionName": "ShakeObject_PositionAngleScale" + }, + { + "functionName": "doStepPostEvents" + }, + { + "functionName": "StopShaking" + }, + { + "functionName": "IsShaking" + } + ] + }, + "propertyDescriptors": [ + { + "value": "", + "type": "Behavior", + "label": "Scalable capability", + "extraInformation": [ + "ScalableCapability::ScalableBehavior" + ], + "choices": [], + "name": "Scale" + } + ], + "propertiesFolderStructure": { + "folderName": "__ROOT", + "children": [ + { + "propertyName": "Scale" + } + ] + } + } + ], + "eventsBasedObjects": [] } ], "externalLayouts": [] diff --git a/examples/starting-first-person-farming/assets/Flat dark joystick border.png b/examples/starting-first-person-farming/assets/Flat dark joystick border.png index 606bf85b223d68dbdd1be4f5ac851f2018e13828..b4e83e112eb9375cefbb86a6d044a972f8bf4dcf 100644 GIT binary patch literal 3751 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 delta 928 zcmV;R17G~79jgbB8Gi!+006uZmmdHC02@$DR7Ff_aWyqHHa0djH8nOiHZ3hJHa0dm zI5|R^?&H|1(F!EEFDZlRnwi_ z{vfi zj=HzjIAJ?^*aIR1yr3o5f0`Hd%5^<6F6Up zP~Fjl;*KVq#|bI9VKHMl8{r`)iPZ%{-xG$jf4=?q~^F0)IA24NSmi2{R2agHyOAVdeoQ zxPbQ(79!wkIWZ#NI!%xsUi&yP0Snv1t6i5;0fp`1zdpu#*d3k2|9q@mKxK#E_j(Kt z!LJ|`GN7)T^niore1Z;j$NmwypId1iKp=wpMU(OX1$5-Pn-+2DQ3 z3U8HfoorJ0VFQ$enSS6!7WRpkSLewMoFS+@5$ws+?STP?b}+=r0-O&JIbb}yTqpDq z_kD-lXD%rF#snxfL8d2)U`M4yN4i*I^UWcQjl?nfK%C*3a$s;&f%6R|JUAMx!11?+{;EmT?0T5AdBGkQj7xl&{aQ(}c)V~^X*e%3^Jziw;PzI z-Q)Pxrd*^ diff --git a/examples/starting-first-person-farming/assets/Flat dark joystick thumb.png b/examples/starting-first-person-farming/assets/Flat dark joystick thumb.png index acd005cc067032996264e510b58c8dbc0c102b62..8acf179f8660be36ccea1198105da35c07154fd7 100644 GIT binary patch literal 2939 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 delta 1267 zcmVk0APj~}1qIg|y;$4-|3eSdR=EXR#Ljv0&8i#El0bNq zCCPtbOc?zjLt;Ha9GISu$7A+MCe}mfMjVETgcv#JMftk%V+^MpZhzV1`Lv#fQTY|# zmVH%Kc?7GE@qgm|MuXXE5Wx|LA|lGXymX>j0mHBaiOCTeWuiGqm?tdkJb~nKEi%ds zbM!h!xMXZ&gS-Jn!ieZO!|zk!l0H6(3xx%l_7e^=V*nBnTyJoR3x(m7Y_~MkS+K|$ z43KnXp6`KzQc6$TjZ&Q@kr{*CB>m&JS3P*(6`NzwM1R5L6lTO&ICHWPon+|X`e7_z zMLC6F0kUhTIljIC`n@PVwP`u5k8%6kln44E_qKxh( zb{b06LXp^Yg%^cvtZ<1NCHc|QMWFQ-3U_9ArK2>SoN9MLNe(uuyLk@p)7%> zJlGHAXO1vp$Jbv&St9IU$JcM8FoX~@Q3#uw_NohXG+4jH|n z0sTU zpp0)pVW8-j`U)3UIhi+}#$BQ8($xh<8D1zSF{H#gGk+;3T}A$ClUFXto{2|3#opr< z1{e9RtJiwXxPn1_o!Y>j4vgs*Rnv1zdY19I z1T&QU(iw}qhn0M_M07d@(=B?wrnEYRR?F(SFg8$}3iw!f8+gbg+55IH7m6mdx_{;G zwJ5GVf)}rJh!K6BOpyHTY)Y&~5puDyja~ZueJI8j_G9+&*0hR%T$yseRbcqgywvf)bNZ*(}g~01Yys?kkB1~sKZ8{!H`|Y zbwYLk4a7gL^N~YT3{4p$1wFQ5i0U&(+omc9F}7{A?VAP;ziABgtA=yGYGnGW2HC&s d-wXbp{R5(SZUFku=BNMw002ovPDHLkV1n@6OJ4v0 diff --git a/examples/starting-first-person-farming/assets/StarterToggle_Touch.png b/examples/starting-first-person-farming/assets/StarterToggle_Touch.png index b38bab4da6aa9be1bf4487046f521efd39f403b6..204462798cece3a787bf29aaf3c77d2867382756 100644 GIT binary patch literal 3329 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 diff --git a/examples/starting-first-person-farming/assets/StartingGround.png b/examples/starting-first-person-farming/assets/StartingGround.png index 1c66f93412b0f53e8c73213298b347157759734a..edec42fec83b5ace1e1abd51c617a430c9a319be 100644 GIT binary patch literal 2824 zcmYM0dpwivAIB$$t(+@tv52%B5<|`wF&hed<`|NeDVds$!QqDG;M|ZZoW1NXHHJ`}Zy$IHGC6Bb6EAuP+amyMTs~c1KQ*K2f zA0p~a-k6&)()c>z3vN!JS{iSRLA125E~sTeK&hCuJ?XS0ttuP5yeE25t`}xSoGkg9 zG0s2)mnnJQmAb3%B#D<&iZq51D0~)qsaD{9P|t;BrgbN)WNR()dGuPNvR`U>*&8LL z7t%ZA9ONq{-ki!HTFy&uTvbPHfd!aEYKFDD9P;# z3|Y)nk}H$g#EGEyWdl1dV>X*`bUM{T4ev4IZ}@bT%`Yit%4#X>CSZ9aUJzVNkZkxOb$HIcC*7hv!#!xJrk;6EDWG?RY*v;0Qccu@y zGg(;k`)90(EK99U^~~|i!W`Ga&?V))aqegOE^kV?SOVYKqS7<%`&>>x^{^b$+6Q-; z#E5pxk<1)t)-F`sstWj4QQ+&D``(T4-PG;M{=YR~`L}(ddSGhLv_DjA=X&l@h5cmW zHAfPj?CMm<-}&ey+oUAlrhgTivEbfyd0LjeK)hNN4wmk8ph9j-1lzm$EhlWx!1$l+ z&JFd^`uE!rq+o^fDMQjRqCt1Wqv|}(fXB$4KF*%0fusETFsM}4AuFrw*VehT75B>d zrp6)lFXYl7PFXc{J*Y76k3PAFY;_gqVc86-DvbIkevBGj7m?vT`p?n$#dlu41>pz7 z?^SN9s^i}6Vg(HHP^wQo4y8M$KM%V%J~V5H6zpLKPfdG--n*i>!;eV!LSgHRic!*Y zlj5{O{P0sNz{Jm^ugO)}J^8)P?90K{dsq;GcGQFsBB~SOW1@kc|8PY&$W+;ntTqEX zTdvU)HGMotNgUwV!M!uN*!Pc%ndLxVs-Z)^J{+AVBDy&D8r@EI){m6b4A>fogx*%` zvSQ6UfGyE@#R*s1#`|@@jvMYlqmf1*w-xlQLt!7Ak6l8PCn9UR^ivC-ibmlK+Tv^O zSBLtR4>QxnHVM28m?ct>O0Sm)44y{1KbV<5YTbP0O(z{n=$F?q2=-sIC9<~D-^Npg z7>hCPxF*~UDM+WcjIH0_xE}SaIIMj4=joc5^EEaNdBeI_eofK6G-+RAennjHls4n3 zEGR}XiLpW^bva){uW0hG13%P|f-HK4!v3;q=_JL-zs$&88MMV7Zj#-B+VwJPs}d52)8-bvob`pxP;5oyXZGc3S*P|Q&PH6qPM*_Aqb>5dj!z8v4=Ubxhwp7`a(qyxTuBjndmjQ(r5gL& zA&}2FvlF&Uc$b)lsjIZ65JJ=-Dj+7WV+evFUX`kA*ma^_<(xeeo8C%3tz$E6II@LC zTL~cy&N8cYEFBxc?M3r0?0S)V^=G9@+>=3XC2{StVVa|h*E)@qA zfPI^Arqkpvd*O;DG8DIVp-6q6b4Ja4^w74YTwo!H?>n{j53dEE?xdSLaQ?H{3f$^%5RbArfj84=#A{1-F*Gg znz~t`4Z$3mcCC2jW-~aOcH{}u=oSRLEtl4lkJr2{`G3y3$m=dO@TiWNlNU=)G{UX#)LJlF9NH{*X4I>~;MVLU z3%D^aCOpyU^iar+Sca{N-S?q}m(x-E85+r~Few<7j%OAtEJF%y_cY9)B@R0Fa9&O> zm%sNQ^gH0}Q1AyS0ad};xlI2H3GnNga&h1P|I#i9{%&;e9s0X<`1{;4|NRB`tN zwO~T=D2m<(&?(~Gd#|pJLPdj~eF4KYqdk`k?f9(n4tNjMk7k*a+IeYq3^+gYXwl!e^u8Dl_NZz+A zTIK#1JY!VhyazZiN?+t9rqo{c03f)7SJ&|iGj^$L=@VF6inUmGsdhC5?(v z)y?H7-a}ZRj*00;{L#S@4D))ui1a=m+7|XvwOy;69@eXj}U0I#r*L2%kX3|z-j=1*~5lT6g_|WF=1Z8105emSh~p% zK0^`m;GF3favHFo&$)b%(f(-I9zTk3AAHm$d;%kf6lBs>YO0bLrnnr;=a!~8R&mti zw<~$Nu%E5m(WvCX%Ot_$w!E3zT!&ynteybjVI zo0~+g3dxZQ`>FZyix9%Ih)?zxb|U$vI>nkl`Wl_3a8Gv#ha4T`%N#9^b&RWoMk(zx z_k0`MZdSPMNqyVyg(u1m=wD%kU4g2RA8<4JW`)OtcKP)|yqiRomAg``(*jS2N?oMr z-7sOqR{~5TW6(jywU1{t_t|v)b4H3~ib?YyzWR`#W6A7gwT4*L(@P^&Nz!O^qH*o) z&)xxLjaws<|GZi%@8>7n9-u(H^+sm(A>!Jl>lCBnyF;&kf3>O6DEH^`Mw6)<`qxRq z#FPrnu5lF^2Te2yMVmkkz+zb8OrY-iGU?ckkpJ-ukZ7ieq5?x$r0M`lojD#ynYixG gj7!vjwP>w_*+eVJaJ4!I;0G0ibw1*B-!UZZKN=)V4gdfE delta 4495 zcmY+Ic{~%~|G?35%S|dr48@!ssF*Z4Gr2NX5{9`qWXYH#rOT0X4AGpCGvv$?W6Erf zzKF0`XpXX4{MPrM-|zRw^Lnn&^L$>P*Yn5w{d&@5N>rZ7>PPeQa&d9p{6Krh=>)

CVi05h7CM;*%6n z%W@BKs!pRwWp*xbp01SgS(5#L-FTpDljotNcnPK*{_2yPFt|=w>Ow zN;BtD`!qqPihCl?3H$&qC9%T*4UD8Xzwf;IUdL+(w;cy(iskP~p}NXI3aZT8?N*E% zQz6O9NePqrO!Xup)iS7$dP7{p9wID$QrE{D@Q`1MOHfGzOlJ(c!9G?BLi8cXPu3C; zqz(4k>FOb!0InnClX+Q^Je#X`($gxA#rRlmx(g>bGffJ%?u7Gvh2IKE` zWez;2$1Gny55QW6c2XT`B60=O60g-9GPLDCsfMDCmBv^z_MK9&ruG3cUmJz5_est7M(~t|=NRZ|3!jqq9rcT`-n@5z zJh)R*dqR*)Umu{slX!NNaK|*jD!kBk6apDzpS^ttjgmC^tx1*MSY9z`ws-_))7F))S+%E-vC-!=RI4dNLnzir3?7s zvxGohlL7suJM#QPm}6E zsOF}iRQuueSP9#Y!F6b9TTtN=XbxjdJI-3on-_^hT=4;kbXX?7LH+3G4xT(e9QD|X zPLG3Rj$1h_Wz8G-_AmG}B54(D6#H#v!+>m6+t6)qF`@*Zdi{b+rb=Y7%^&$#RRNg} zY{0u@twN<6J;1(*XF`>3kK~1!l{yB zH@*-YtdgVeg;&=x4W~oR>%J0XTtR&0KR#&}?f~C__-uMlKWiO=u?;igd!g&e1EpyL znBl_WH;R{oG)=8YW1@b1YsgyP{^&<)HAvc1R$wKNC+QuxBRlEAOn?>GIWa2XUWuVs)`TvkR4J&WGQvh8p^s(QEO{tgiPh z6AW%*^rhf+r{vSp9d?O4W`cRl#99GYU|$GQ2CU5R9Du4hkkD)blT25Bteoqq3Frq% zY69%biUo?AUcHAVDw}Ek+DFZ?NH7xm&Uq!Q8MqN5^*c%jh+!;+7B=m5!!~2ss4rj< z)D2SF*cBnA?Ctn<0E%ll0YI;1Y&o7D6>T{y+M@ZVXtGMZ?~P%LLdbofP%S6@yhS;J z>3HjS8L4eoPf}SdZHR|r#PDZ3Wlg1ot^7v6UtmI#^Z8FxR*cfaw8o?M0!Pq0=>`!) z$=uvhHb~4NGAw3=K+00GK#}`@a$<^hHwK}({+!}1n3QF!@S8Yd8wve%fnalH0fD5c zuvWK#Z)C}fjP#v5@sZ>iQLwQJoO#ZCMh0vIUr)goKW^G9fQ6;AmvAkz@n{83q;FTq zb{3Krzqcx9n2u2&=M0`3A;Q?rF=Wgl{$VpT9&JpX(FCvOH|-H%VPota4O|N*enuV6 zT!w8L?X9*OG1~V6!*IlT*;N+j79$3X6$s@_#aG)|tFeZRX;z>Pj!1@%^}*`jK|j+$ z(eX1E;azIrko=}8R$vcqca7cbO!lD@LLzDN!@hKaRs^l_#F7HW_UQK@9A1G06E@T0 zv*>FcOS!RbcS>gr`gEz*Fgt14=nb#UikG%R-p>fwq)~YzJ z;ytXa{WIG>!6Cn$WR*!>o{mc~(WR?EitrmlFo5~+?7e4H;jvTaJE?A4)Kti@;;exA zHa#ouW*UB@4~9_{2x{{c{Q3m?-#ZaG7v8YnyW0P;*l6!x;y8ziXw3spD(N z)XF$7tGB1Xvjt7oQ8z}kAo`Jp%E?3LrFnW34@47CSJwPLwrG#nY*Rc9Z2;IupjMKc zaE#9ETiuH2_zoSo7U^o!*Hg(kwZ`JJY@$krj z<=x~K*eG*y8JBfRlaw6dC48QLdW9nWpa>s20;8_L1A%WQ9u78!pj2x`1X>d~Z*e?4 z2h;Ac6d)TiO8h&b6sNUbdd^f39ML2b8Q&ofuZV-tex{qkr)2;=%2>}?gSe+ehmoO6T{&{dtr$@KHej&G+QF>72ac2 zns-naW_g%lPjtye`T~4pSJe`owHXeW?>?_jaKKkvHB}v?0Jtb}$|mj>&;6=wG zj$zZdL|=BQ_5}f5pQsZ1_}|`q;`4+uwT%FN@7pQ)f-M^yL^okOENy1vX6qh&I%Y1I z(BF7n{5N!u(NcQ#>cQG}!`5ziMGz^jRI=0bv+USpH%f5dXKyQNcKPx$DVOiL&P@fU z?zmWc*48p9X*gF$RO4p%rOG%AYiqZT_~$@f*dms;*|ziIk~?KB>x%MNndGJ?E*gM~>V`O2%KMy(+Mf-+A_Apj=CAksN}CG( zcDr*Lq};nH*_rPWl3gHtfeK4u0V}-?dTAHQw~xC>9%}nCL#fkxtC-PYiZoHCw~q=| zZVMt}uRE8V6iQvto<8rjzH+{_-gOPOn3W7fwr9bfq$ilkOMWa@#yyU#RN(>j#`{RF zUEZy*#9n&iULNciVi%*1p)yPDg=8GC0n^k-Z(l2pY}78FNA>cRd%VaDkVGMazP>}4 zYDKG4v&sYAGjdpHm_MK(-Ru0F%IV4PN&;In^9#FP!;zpfC!Flmj;zke@J;Py>;JGX zS^r4~$XUd$pOOr1&$Ntv|9V;nrkc2kaPHO&*+S$1GCWqU&3(jZvR0GUked||*irSx z+GO}23g|j&q2XkDX_kU(uECS$4#VLuWKxi|k+=Bqx^oW%0YUlbjevB2x2feb~3> z?4`?5?J*%e6-Jn5Inhyt$3fmh(`P@OQPDx&=I!p)on}@2py1~92%yd#>Z4eEW_Vx2&&+edw255yFHQ;>!W`s-{;B>y&KaDTXeDrtkAsD)rg;7ySeSC zkFTv4=DB>05R_vdQ@!S^oJq1=P6>Rs+m(qosf_>X`5E(hiEm))Zz5N7Yp5Lh&e?$d z;qI%lvKmEA+H`_W$h`m_3yOyBhZW}JzMAk=d4ir3FPRJvSKeRZlntqh9+i3{*%)np0Sg;0nc_n(}cGdkZ%d;wj0($bY zUXZ_WI*#~MV)YMuM`UxRMWF6ypecYDUktME#Z@)aGwgpXGUL@;gZd3dqRPRI% zH?_bLzqvIlyLR7e==dV28)5o46(7Ig=^mqRekv;F+?F*}H}-wUwQr0FT0N^ut$WYk zZ`+SIv1iMAjvvmv*EqMyTKzTGB2!+Hk|B$03hjrg#-=Dvm+gPhjJ<**{t{cQX77+N zpU;tnvx=HR-$Q#&cXo3QUw|`1%sbSkvt z{}E-x_{2s9M!o)L0`M-a9~{1W{UdXFz1Dcn-jESOk-Za)BTh;N7XGgZ)Bq;lvp9QT z_Kq-)*h50!#VisgmutJ2lsThxjLNtIYn5t}CV<;wHQczC@sZt%WZWj3Z<)H_G8F1o%QNoKENpm$?WwJ_PTQ0EgiJ;eQ^t zTg`6v`-lI(ku(k=U5wI;@IQxOWGKdcvDQywsY0{V6oSLI_QPIEYe^_}Ky$Y#{)RPg zMM0I2Khe3WDkO2_@9=oEF8uXRtLs#+7CtAPQTliGP5`;O(31K9SzP*!`*|db6Cwtm z&Re^5w3uqNbgl$)*P!)C5$Yd*M_Yn(ond;UK&{si?W7uQuck-Ayqqxv|6fwBk0-#pBrD< zs3@|s8NYc=bg5@%QG*zzs{HbbZr#Iy48AkxWLKl*2?A0a9SF}GvC^G3|Mi~@sc%)a z@X@1h;xGe4)%;hLwyYP2ts3&&bx&nacCBW^eXrRM32CpittwWe>FUrN3f<}NOh?y^ z!aR;(#mksAC%*H9X(+sDd0rE6;xkHLrPF2m?qX+%ktO%_D%G4#M}mApI_N@OlIhu_8gYGlW|iIk2R_55 z9>QsVQ~{`PSxw^iA z8Lc`+_4fj$*(pDk86E$Z@vN;94)p)O12Ejve;q(w=Nwe;wz8+FwCvxDYOm=ZAI4}t zeZox*duo4i!WD|haxk*FukS<@EjCel##`q!c+%h-I-rYKrbwA#w7);#G5-xxWVu^S Ra6VVKtjz5ougtu${{s@(*f;)7q>*8F_g}a_-yc4o&-r|Q{G8AEWO;d@RFsY=0RW)l>f-Dp z)2e@=s37AUex#{P_TyYG#sPq``o91HGDTY!f^j}5IKbfPev%ovNJn=^0C<+Kyb~rb z^9SM)f$_fBh{S4HJrWaE@vI~q0OXmj&W?Tw;IiNx);B0(g6AiNsb-6=W%w>ANTIQOi4J`kWjl#f`<& zxV3w%ONnP)3#}_K9CUu7Qa#Kv7|)31{_<3(9({vkfZ1Je%4LW67(3`q)u^r-b~zcX z!e0hOAnAux9#?iIoXn5d1-u`4}|&a){!P&Ut6O-Fj%=@XRD59On!$KLlI{6O)a7BmtkvvnKr3K zbPpg8&JW$RJ@!Ji874s#GhgO(bF@A{8gfZ`#+3a$qojo(2!7pm?fRMq`;1ZsGNgUSBBda%R&dXIlj0shQf(ByS=WKQ zQM8?T*>CK=94~26554gYWH3y026Av%Yqq$b(Iz)9l%UbU=2cqqoA+$%5dK|#eXU^o zHH|1|ft!3;!*_@LH_0c_#t!eL@YI464lW0e*C?v_$SE5AkbVe+X9GVX{*V82Cw?^G z>F5)%{XR7|4o&kezEKIbFG%Q}BQ4RXJo3df1qB6_h2DVy+|+RSm?vQ8BZYD5mtsa=Z4S< zNhY+mMClY~d9oHtAe9r=Hl}|MH*})hg22k!ra!hZ-&z#Y&(BrZld{zGc&yDu&Nq4_ z^iF9sB4J~uPy|hxm0gTi$go+P{S|H!hUfO!)7^8M<%}I(zoI;g9Wv~jia}dY5w~)L86#0cov&-`xs{wHGk56}U$VIu nQ~1a;%9N?jB0T~Dx9xqkALDSP(B~A{y#uZY4`&8E^tXQimD|0$ literal 6328 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 FV1j1w)3N{n delta 627 zcmV-(0*w8$9DxOp8Gi!+006nq0-pc?05(ueR7Ff_aWyqHHa0dmI5;&mH8L_XHa0dg zGBP$cHa0dk|NsAAUtjX_^4{Lw#l^)&Mn->se~*ukx&LcZ0000AbW%=J0B>IZjxp~R zy4HO*rZ@lq010qNS#tmY3labT3lag+-G2N400G-cL_t(|+JD8F>ypAC4227X5~x;v z|ChDy&bnGlZe(`P@6Ii$Vi%H32h?z{` zA&({os!ck)jd&rN1@*kv&X;~w(7+>zIGK0^u?M`ITYobVd%(4ET6Mt2>z{${OJ<6^ zsnagq<8`6Sd|rA-EH^URbgQvHQ^O|E%x{@$LOs$9eVLZ5Rh; z*XQ!VgKQZc-=f2o!?Ed*2lmdfbz>*?te|XMb>FPA<&m<}EJWI#E9%{YoXZKS(#-Mi zEz^go>*bgJ1}U?hv%<;dkt!VPW>+x%4v0{XX>l^EM0ovUQ4t~&+a0mURTHt&FNy#F N002ovPDHLkV1nr&BUJzZ diff --git a/examples/starting-first-person-farming/starting-first-person-farming.json b/examples/starting-first-person-farming/starting-first-person-farming.json index 7dfd614d4..041b3c57b 100644 --- a/examples/starting-first-person-farming/starting-first-person-farming.json +++ b/examples/starting-first-person-farming/starting-first-person-farming.json @@ -1,7 +1,7 @@ { "firstLayout": "", "gdVersion": { - "build": 272, + "build": 274, "major": 5, "minor": 6, "revision": 0 @@ -377,10 +377,10 @@ "gridColor": 10401023, "gridAlpha": 0.8, "snap": false, - "zoomFactor": 0.31119749424453913, + "zoomFactor": 0.5468750000000338, "windowMask": false, "selectedLayer": "", - "gameEditorMode": "embedded-game" + "gameEditorMode": "instances-editor" }, "objectsGroups": [ { @@ -1852,6 +1852,31 @@ "variables": [], "effects": [], "behaviors": [ + { + "name": "ButtonFSM", + "type": "ButtonStates::ButtonFSM", + "ShouldCheckHovering": true, + "State": "Idle", + "TouchId": 0, + "TouchIsInside": false, + "MouseIsInside": false, + "Index": 0 + }, + { + "name": "ButtonScaleTween", + "type": "ButtonStates::ButtonScaleTween", + "Scale": "Scale", + "ButtonFSM": "ButtonFSM", + "Tween": "Tween", + "IdleScale": 1.2, + "FocusedScale": 1.25, + "FadeInDuration": 0.02, + "FadeOutDuration": 0.05, + "PressedScale": 1.15, + "FadeInEasing": "easeOutQuad", + "FadeOutEasing": "easeInQuad", + "PreviousState": "Idle" + }, { "name": "MultitouchButton", "type": "SpriteMultitouchJoystick::MultitouchButton", @@ -1860,6 +1885,10 @@ "TouchId": 0, "TouchIndex": 2.0247e-320, "IsReleased": false + }, + { + "name": "Tween", + "type": "Tween::TweenBehavior" } ], "animations": [ @@ -2800,6 +2829,25 @@ "TouchIsInside": false, "MouseIsInside": false, "Index": 2.0247e-320 + }, + { + "name": "ButtonScaleTween", + "type": "ButtonStates::ButtonScaleTween", + "Scale": "Scale", + "ButtonFSM": "ButtonFSM", + "Tween": "Tween", + "IdleScale": 1.2, + "FocusedScale": 1.25, + "FadeInDuration": 0.02, + "FadeOutDuration": 0.05, + "PressedScale": 1.15, + "FadeInEasing": "easeOutQuad", + "FadeOutEasing": "easeInQuad", + "PreviousState": "Idle" + }, + { + "name": "Tween", + "type": "Tween::TweenBehavior" } ], "animations": [ @@ -6187,6 +6235,10 @@ "name": "ButtonFSM", "type": "ButtonStates::ButtonFSM" }, + { + "name": "ButtonScaleTween", + "type": "ButtonStates::ButtonScaleTween" + }, { "name": "Effect", "type": "EffectCapability::EffectBehavior" diff --git a/examples/starting-first-person-horror/assets/Flat dark joystick border.png b/examples/starting-first-person-horror/assets/Flat dark joystick border.png index 606bf85b223d68dbdd1be4f5ac851f2018e13828..b4e83e112eb9375cefbb86a6d044a972f8bf4dcf 100644 GIT binary patch literal 3751 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 delta 928 zcmV;R17G~79jgbB8Gi!+006uZmmdHC02@$DR7Ff_aWyqHHa0djH8nOiHZ3hJHa0dm zI5|R^?&H|1(F!EEFDZlRnwi_ z{vfi zj=HzjIAJ?^*aIR1yr3o5f0`Hd%5^<6F6Up zP~Fjl;*KVq#|bI9VKHMl8{r`)iPZ%{-xG$jf4=?q~^F0)IA24NSmi2{R2agHyOAVdeoQ zxPbQ(79!wkIWZ#NI!%xsUi&yP0Snv1t6i5;0fp`1zdpu#*d3k2|9q@mKxK#E_j(Kt z!LJ|`GN7)T^niore1Z;j$NmwypId1iKp=wpMU(OX1$5-Pn-+2DQ3 z3U8HfoorJ0VFQ$enSS6!7WRpkSLewMoFS+@5$ws+?STP?b}+=r0-O&JIbb}yTqpDq z_kD-lXD%rF#snxfL8d2)U`M4yN4i*I^UWcQjl?nfK%C*3a$s;&f%6R|JUAMx!11?+{;EmT?0T5AdBGkQj7xl&{aQ(}c)V~^X*e%3^Jziw;PzI z-Q)Pxrd*^ diff --git a/examples/starting-first-person-horror/assets/Flat dark joystick thumb.png b/examples/starting-first-person-horror/assets/Flat dark joystick thumb.png index acd005cc067032996264e510b58c8dbc0c102b62..8acf179f8660be36ccea1198105da35c07154fd7 100644 GIT binary patch literal 2939 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 diff --git a/examples/starting-first-person-horror/assets/StarterToggle_Keyboard.png b/examples/starting-first-person-horror/assets/StarterToggle_Keyboard.png index 54eb525bd31884a3c9174fd55987b5d12d415c2b..aabe87250073b2858bdb31fa561d3cd73a5c2456 100644 GIT binary patch literal 3413 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 delta 1267 zcmVk0APj~}1qIg|y;$4-|3eSdR=EXR#Ljv0&8i#El0bNq zCCPtbOc?zjLt;Ha9GISu$7A+MCe}mfMjVETgcv#JMftk%V+^MpZhzV1`Lv#fQTY|# zmVH%Kc?7GE@qgm|MuXXE5Wx|LA|lGXymX>j0mHBaiOCTeWuiGqm?tdkJb~nKEi%ds zbM!h!xMXZ&gS-Jn!ieZO!|zk!l0H6(3xx%l_7e^=V*nBnTyJoR3x(m7Y_~MkS+K|$ z43KnXp6`KzQc6$TjZ&Q@kr{*CB>m&JS3P*(6`NzwM1R5L6lTO&ICHWPon+|X`e7_z zMLC6F0kUhTIljIC`n@PVwP`u5k8%6kln44E_qKxh( zb{b06LXp^Yg%^cvtZ<1NCHc|QMWFQ-3U_9ArK2>SoN9MLNe(uuyLk@p)7%> zJlGHAXO1vp$Jbv&St9IU$JcM8FoX~@Q3#uw_NohXG+4jH|n z0sTU zpp0)pVW8-j`U)3UIhi+}#$BQ8($xh<8D1zSF{H#gGk+;3T}A$ClUFXto{2|3#opr< z1{e9RtJiwXxPn1_o!Y>j4vgs*Rnv1zdY19I z1T&QU(iw}qhn0M_M07d@(=B?wrnEYRR?F(SFg8$}3iw!f8+gbg+55IH7m6mdx_{;G zwJ5GVf)}rJh!K6BOpyHTY)Y&~5puDyja~ZueJI8j_G9+&*0hR%T$yseRbcqgywvf)bNZ*(}g~01Yys?kkB1~sKZ8{!H`|Y zbwYLk4a7gL^N~YT3{4p$1wFQ5i0U&(+omc9F}7{A?VAP;ziABgtA=yGYGnGW2HC&s d-wXbp{R5(SZUFku=BNMw002ovPDHLkV1n@6OJ4v0 diff --git a/examples/starting-first-person-horror/assets/StarterToggle_Touch.png b/examples/starting-first-person-horror/assets/StarterToggle_Touch.png index b38bab4da6aa9be1bf4487046f521efd39f403b6..204462798cece3a787bf29aaf3c77d2867382756 100644 GIT binary patch literal 3329 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 diff --git a/examples/starting-first-person-horror/assets/StartingCollisionArea.png b/examples/starting-first-person-horror/assets/StartingCollisionArea.png index 0cf1020205023234d56757be2c4eed0e05d7002d..443292ad3a56caa45af6177c5c49bc749794facf 100644 GIT binary patch delta 753 zcmV|gm<#igRwFyugrlh>ilg7#fNLFgUk6GgZNLK0~ zJBHCCAX%w{E?C9_kZhGP22m-sFPiYXZ7s|5%ERJZjLM041+}XuyfhQl&Kkag_&CCqPo3 zqft(r0BL!SMmcr@{{Og$6V66CG5~3L&PF*DfD7`R3)a9LkTiMF3m}Q|pc_C^}5GzPi00000NkvXXu0mjfQ3_BV delta 817 zcmV-11J3-22FC`FBYy#fX+uL$Nkc;*aB^>EX>4Tx04R}tkvmAkP!xv$rb<;R3U&~2 z$WWau_(B||ibb$c+6t{YnB4RQO&XFE7e~Rh;NWAi>fqw6tAnc`2tGjE1t&!pDe>H- z&?3eIm;3SG^Z(p?E+Ev)Of@^k0ade%WGpIXva4d?72Ozs4}blbkeI2*Q;TVM*4I6C zQr$&(mUrKu)vM%82KYqcIi?#H@dokCrloV!6?xAV%r}h zKwuYW)NK3v*tQ!dfd3h|(pvsX9hm(jz1GsgM?l{;aBU!s02y>eSad^gZEa<4bO1wgWnpw>WFU8GbZ8()Nlj2!fese{00ACJL_t(| z+U?pw4u66$6olciVdKR(mk06$p3Q|j3<0DqZGG>}uyRMfKN2*Z+w+dcJYo3tdVfFv z^Ztn$4*-c7KTm@LSi=wf23W)o3t$yLB!Fdn_W;)MT?2T)cMISJ-z9)2eDeU_@J$1F z#5W7z72hO)XZ+;=-tm_LM8H=Eh=Q*Q5D8x#Ab%RZC_qGfc7Uk(tN@Ymrvakle+FQ{ z9|mB-?*?GPZvwF4R{Go>JeZ)B~HwEK>Gmg1GEod#t#dieSqOtt@Z)T z_-+BT4{-GJTKfQIe6s-B2RQyqt$hG9zB+*R0YUJ^0kjVYhR+V5eLztBX#nj5g5wVZ vXeu8-g5LztK7b6r1kgT!6u)`|SWk&R!a@PWh3-$;00000NkvXXu0mjfQ@3cv diff --git a/examples/starting-first-person-horror/assets/StartingGround.png b/examples/starting-first-person-horror/assets/StartingGround.png index 1c66f93412b0f53e8c73213298b347157759734a..edec42fec83b5ace1e1abd51c617a430c9a319be 100644 GIT binary patch literal 2824 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 delta 4495 zcmY+Ic{~%~|G?35%S|dr48@!ssF*Z4Gr2NX5{9`qWXYH#rOT0X4AGpCGvv$?W6Erf zzKF0`XpXX4{MPrM-|zRw^Lnn&^L$>P*Yn5w{d&@5N>rZ7>PPeQa&d9p{6Krh=>)

CVi05h7CM;*%6n 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 literal 378 zcmeAS@N?(olHy`uVBq!ia0vp^4Is?H1SEZ8zRdwrY)RhkE({(F?hNicYo9j)MK}vQ zB8wRqxP?KOkzv*x37{Z*iKnkC`ztmf9$D`E|670}jO?B+jv*Dd-rn8F*=iut7C2Q$ zP`v9mf8wv||V~00!*A{PeEu8xM;`w=bRd-)koZnsVy{m5C+^YZ2 z1Uh(uCNVKMFfeccO=M7DU|<0`QGhJLAMywO=-=p++8q2|e`Wn)>BE9c@^{)RRP6uF v-1ObLsfL*vmO$Mo$MBAyl|q5$ zM_V-s{Y8`(QdOyvhA8Qewh3uMN>D&1`V_3mA4_LIi5d-p!(e)r5dXU^O?21q}cr}h@Ei{&W+ zEP1-*>r$|A;lhHZrlx#T(>C*6?_-Y1Te@^9)q&NkSF3YA_0&`5I_|Zll-_n_L{|rH4E@MlVVTlO|9 zNoW#XQXxg~M0Yd_7}Nt|)D@^^sH~0E3LOt0hknj{X+kzkffkYiRi{TGlN- zSk}xyLP$zyb64lVUqVa@jU*C@%6d=a>WnIZMh6gS=4p_zFvo;KAzoL%_({?$Q6EIDHH@Km$sbwqZAttFO zVuxs%$iUI93;SonsvUFgoucnC4)TKZO3`1~QWP zm6O-O7<0Wih|`N+vmGyi+_kp#i+@PU7Oo?p5x$?<18?CF(193n!uzxNz(*c5JlfC& zYU=B&nPoi=*BQ`PY;!ED8e)dU<-@~~0ym0Lml46w-rin|^TPM0w5yUT(8b}b;Exp* z)w4nKp)Ire)ey-&-PX7(?kTLInH#R9F|FUAc;(EBQgYWuj8yb*vC0!zR6CHrhhs#3 zvEQyOE-E?S7Z=ajpwV-`Z>%4kBsons1N(+XMH*+V9*A*#L18C@do>^g7baxGYqYzk>v}{wrJE zm&keaksnXE-@W%e0Q>gs2fu*>x{tdKw|`9sVwFKL#HqTvy6L7sQQkix3!pB$^Y?Ej zv@hyvSbg^zIDPsI_zfrOeBFYhScWjxB~R6UF)_0g>lq@(hB(F zy3o!2Vp|(z3S^m6vFNG5l2$8D%Ij%L9%z9K!?-Jn`g)VO4q5h0UkB~Q5_G0xZyGR= z8FgR$lnes7H&1W+8Dtn`sQ$DcD4`582)Y@+$TJ}KD5E|ildY)!lmt?oKVuR|?#g6+ zy;MO_oy>*6VpAbzB$5#cq{dSSDx09|ue$RN$N(11n>R1<7c7$nNnN(dGg$w~)dAXc z!TTnC+Cciw2PK{`$cRm5%sm#)oH?TiOxv0X13%n|WI^Pm+o)7lR>q77Mp}CyjmT7Y znJgOW>mhBB5ITGItl+2!rM4((!;#KUC)|ey|9f%Vx_6ReS2{k?@$dgmr*F6~nG!k{ zpnklttRe_$bY)(BeY+Z$#zWmKhMlPHM3RX_veah!G8GVcbUjiTB>A_kTmd)Z`5NMS z0bT3Kjhi5SU?KWTi~Xd)FX=SLAAYzSzH$31jJm{T*FM<&kxN*nKem}5!Bc%Tem^BK!nnRq>>3pDcY%4g6G(>9dbwt zw(SJ5lFE&_90}(LOcGZ@VOq!-^8+uOJQ+fn3_=bG0o}f!Wko_54~$75xiUh+$B;up zkdo~|A5hW?P(>iRGSac_KSK@)!HGuy0{We4S|_Gn7VPfo*n@Qla+mKa=QF<>`>51=tC#pzNBD9{*tOWO6oZ#99uPvl0@pdPpFTG<6h46;mA9ffa^cN?q!z_eE)~kgfje9C=PxQRC@K6a zZqE3-GuI|$&(Xu}i_!8rINY^icm=zv6=wh=mil`wGR>D*dBwA9^7HbjD`)iek82R2 zNHltD^vcEK#l^)A+6D)uPv~|3Zt^mBxvO5*Zqmi66d(l!1)}F@#|a^v$KX1H5aPL^ zaPJ9}JTe@zqM}0SJ)MNZs681kfdsE>gWY((H8eD2mCl~`IJekO;W_~6y>RcDXV0HM zZRsyE`o%xz+*$|I7-Tq)b+)gkE_>?K zDYcjew9n_so8TgTGS2&>tgWpTMMXuXOWVgf53a|UO=8Q5rXVmH^)`}6>Pl$xl(FzE zLcePMeAM@1JSjfhp&q(6v?N01j;XD0eV%jnAWS1@$s_X2FwGsVdD1|c1?r+Bpm z65NcIiY_{pqu3$zFxKI{JPX4TCp`B$(rF$go7W1@a6F3 z%gfUJa*`iF0SOLa+flFYzNRArVEgTQWrdXvg>#k zdh5cOLS0(OS1+EsaQjJYJAQKt3k%hKQKDLisV&oWU>d?&;$w7c}qh@8{?z z@@O<_Sw&?FN{Wl$xmJgM6k&?mLyz@`dfP8txZrkKh()8w-&5q7QO0OGFCK}e0EICe zZB-}>4P!cy3xt-dA!|2aQ!bT)W5vY|JH&^A(kQ}a|YHk@TNfp98z95zQt5`&a+~002ovPDHLk FV1j1w)3N{n delta 627 zcmV-(0*w8$9DxOp8Gi!+006nq0-pc?05(ueR7Ff_aWyqHHa0dmI5;&mH8L_XHa0dg zGBP$cHa0dk|NsAAUtjX_^4{Lw#l^)&Mn->se~*ukx&LcZ0000AbW%=J0B>IZjxp~R zy4HO*rZ@lq010qNS#tmY3labT3lag+-G2N400G-cL_t(|+JD8F>ypAC4227X5~x;v z|ChDy&bnGlZe(`P@6Ii$Vi%H32h?z{` zA&({os!ck)jd&rN1@*kv&X;~w(7+>zIGK0^u?M`ITYobVd%(4ET6Mt2>z{${OJ<6^ zsnagq<8`6Sd|rA-EH^URbgQvHQ^O|E%x{@$LOs$9eVLZ5Rh; z*XQ!VgKQZc-=f2o!?Ed*2lmdfbz>*?te|XMb>FPA<&m<}EJWI#E9%{YoXZKS(#-Mi zEz^go>*bgJ1}U?hv%<;dkt!VPW>+x%4v0{XX>l^EM0ovUQ4t~&+a0mURTHt&FNy#F N002ovPDHLkV1nr&BUJzZ diff --git a/examples/starting-first-person-horror/starting-first-person-horror.json b/examples/starting-first-person-horror/starting-first-person-horror.json index 389a2b130..6544d35a4 100644 --- a/examples/starting-first-person-horror/starting-first-person-horror.json +++ b/examples/starting-first-person-horror/starting-first-person-horror.json @@ -1,7 +1,7 @@ { "firstLayout": "", "gdVersion": { - "build": 272, + "build": 274, "major": 5, "minor": 6, "revision": 0 @@ -261,10 +261,10 @@ "gridColor": 10401023, "gridAlpha": 0.8, "snap": false, - "zoomFactor": 0.26364576737709594, + "zoomFactor": 0.47893418845355384, "windowMask": false, "selectedLayer": "", - "gameEditorMode": "embedded-game" + "gameEditorMode": "instances-editor" }, "objectsGroups": [ { @@ -349,13 +349,13 @@ "angle": 0, "customSize": true, "depth": 0, - "height": 120, + "height": 104, "keepRatio": true, "layer": "MobileControls", "locked": true, "name": "JumpButton", "persistentUuid": "c641f19e-29c5-4424-be1c-656b5505f1ac", - "width": 120, + "width": 104, "x": 1110, "y": 576, "zOrder": 8, @@ -1176,6 +1176,31 @@ "variables": [], "effects": [], "behaviors": [ + { + "name": "ButtonFSM", + "type": "ButtonStates::ButtonFSM", + "ShouldCheckHovering": true, + "State": "Idle", + "TouchId": 0, + "TouchIsInside": false, + "MouseIsInside": false, + "Index": 0 + }, + { + "name": "ButtonScaleTween", + "type": "ButtonStates::ButtonScaleTween", + "Scale": "Scale", + "ButtonFSM": "ButtonFSM", + "Tween": "Tween", + "IdleScale": 1.3, + "FocusedScale": 1.25, + "FadeInDuration": 0.02, + "FadeOutDuration": 0.05, + "PressedScale": 1.2, + "FadeInEasing": "easeOutQuad", + "FadeOutEasing": "easeInQuad", + "PreviousState": "Idle" + }, { "name": "MultitouchButton", "type": "SpriteMultitouchJoystick::MultitouchButton", @@ -1184,6 +1209,10 @@ "TouchId": 0, "TouchIndex": 2.0247e-320, "IsReleased": false + }, + { + "name": "Tween", + "type": "Tween::TweenBehavior" } ], "animations": [ @@ -2569,6 +2598,14 @@ "name": "Animation", "type": "AnimatableCapability::AnimatableBehavior" }, + { + "name": "ButtonFSM", + "type": "ButtonStates::ButtonFSM" + }, + { + "name": "ButtonScaleTween", + "type": "ButtonStates::ButtonScaleTween" + }, { "name": "Effect", "type": "EffectCapability::EffectBehavior" @@ -2632,6 +2669,10 @@ { "name": "Shooter3DMultitouchMapper", "type": "SpriteMultitouchJoystick::Shooter3DMultitouchMapper" + }, + { + "name": "Tween", + "type": "Tween::TweenBehavior" } ] } @@ -22236,6 +22277,7296 @@ "variants": [] } ] + }, + { + "author": "", + "category": "User interface", + "dimension": "2D", + "extensionNamespace": "", + "fullName": "Button states and effects", + "gdevelopVersion": ">=5.5.222", + "helpPath": "/objects/button", + "iconUrl": "data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iVVRGLTgiPz48IURPQ1RZUEUgc3ZnIFBVQkxJQyAiLS8vVzNDLy9EVEQgU1ZHIDEuMS8vRU4iICJodHRwOi8vd3d3LnczLm9yZy9HcmFwaGljcy9TVkcvMS4xL0RURC9zdmcxMS5kdGQiPjxzdmcgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIiB4bWxuczp4bGluaz0iaHR0cDovL3d3dy53My5vcmcvMTk5OS94bGluayIgdmVyc2lvbj0iMS4xIiBpZD0ibWRpLWdlc3R1cmUtdGFwLWJ1dHRvbiIgd2lkdGg9IjI0IiBoZWlnaHQ9IjI0IiB2aWV3Qm94PSIwIDAgMjQgMjQiPjxwYXRoIGQ9Ik0xMyA1QzE1LjIxIDUgMTcgNi43OSAxNyA5QzE3IDEwLjUgMTYuMiAxMS43NyAxNSAxMi40NlYxMS4yNEMxNS42MSAxMC42OSAxNiA5Ljg5IDE2IDlDMTYgNy4zNCAxNC42NiA2IDEzIDZTMTAgNy4zNCAxMCA5QzEwIDkuODkgMTAuMzkgMTAuNjkgMTEgMTEuMjRWMTIuNDZDOS44IDExLjc3IDkgMTAuNSA5IDlDOSA2Ljc5IDEwLjc5IDUgMTMgNU0yMCAyMC41QzE5Ljk3IDIxLjMyIDE5LjMyIDIxLjk3IDE4LjUgMjJIMTNDMTIuNjIgMjIgMTIuMjYgMjEuODUgMTIgMjEuNTdMOCAxNy4zN0w4Ljc0IDE2LjZDOC45MyAxNi4zOSA5LjIgMTYuMjggOS41IDE2LjI4SDkuN0wxMiAxOFY5QzEyIDguNDUgMTIuNDUgOCAxMyA4UzE0IDguNDUgMTQgOVYxMy40N0wxNS4yMSAxMy42TDE5LjE1IDE1Ljc5QzE5LjY4IDE2LjAzIDIwIDE2LjU2IDIwIDE3LjE0VjIwLjVNMjAgMkg0QzIuOSAyIDIgMi45IDIgNFYxMkMyIDEzLjExIDIuOSAxNCA0IDE0SDhWMTJMNCAxMkw0IDRIMjBMMjAgMTJIMThWMTRIMjBWMTMuOTZMMjAuMDQgMTRDMjEuMTMgMTQgMjIgMTMuMDkgMjIgMTJWNEMyMiAyLjkgMjEuMTEgMiAyMCAyWiIgLz48L3N2Zz4=", + "name": "ButtonStates", + "previewIconUrl": "https://asset-resources.gdevelop.io/public-resources/Icons/753a9a794bd885058159b7509f06f5a8f67f72decfccb9a1b0efee26f41c3c4c_gesture-tap-button.svg", + "shortDescription": "Use any object as a button and change appearance according to user interactions.", + "version": "1.4.1", + "description": [ + "Use the \"Button states\" behavior to track user interactions with an object, including:", + "", + "- Hovered", + "- Pressed", + "- Clicked", + "- Idle", + "", + "Add additional behaviors to make juicy buttons with animated responses to user input:", + "", + "- Size", + "- Color", + "- Animation", + "- Object effects" + ], + "origin": { + "identifier": "ButtonStates", + "name": "gdevelop-extension-store" + }, + "tags": [ + "ui", + "button" + ], + "authorIds": [ + "IWykYNRvhCZBN3vEgKEbBPOR3Oc2", + "gqDaZjCfevOOxBYkK6zlhtZnXCg1" + ], + "dependencies": [], + "globalVariables": [], + "sceneVariables": [], + "eventsFunctions": [], + "eventsFunctionsFolderStructure": { + "folderName": "__ROOT" + }, + "eventsBasedBehaviors": [ + { + "description": "Use objects as buttons.", + "fullName": "Button states", + "helpPath": "", + "iconUrl": "", + "name": "ButtonFSM", + "objectType": "", + "previewIconUrl": "", + "eventsFunctions": [ + { + "fullName": "", + "functionType": "Action", + "name": "doStepPreEvents", + "sentence": "", + "events": [ + { + "colorB": 228, + "colorG": 176, + "colorR": 74, + "creationTime": 0, + "name": "Finite state machine", + "source": "", + "type": "BuiltinCommonInstructions::Group", + "events": [ + { + "type": "BuiltinCommonInstructions::Comment", + "color": { + "b": 109, + "g": 230, + "r": 255, + "textB": 0, + "textG": 0, + "textR": 0 + }, + "comment": "The \"Validated\" state only last one frame." + }, + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [ + { + "type": { + "value": "StringVariable" + }, + "parameters": [ + "State", + "=", + "\"Validated\"" + ] + } + ], + "actions": [ + { + "type": { + "value": "SetStringVariable" + }, + "parameters": [ + "State", + "=", + "\"Idle\"" + ] + } + ] + }, + { + "colorB": 228, + "colorG": 176, + "colorR": 74, + "creationTime": 0, + "name": "Check position", + "source": "", + "type": "BuiltinCommonInstructions::Group", + "events": [ + { + "type": "BuiltinCommonInstructions::Comment", + "color": { + "b": 109, + "g": 230, + "r": 255, + "textB": 0, + "textG": 0, + "textR": 0 + }, + "comment": "Make sure the cursor position is only checked once per frame." + }, + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [], + "actions": [ + { + "type": { + "value": "SetBooleanVariable" + }, + "parameters": [ + "MouseIsInside", + "False", + "" + ] + } + ] + }, + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [ + { + "type": { + "value": "BooleanVariable" + }, + "parameters": [ + "ShouldCheckHovering", + "True", + "" + ] + }, + { + "type": { + "value": "CollisionPoint" + }, + "parameters": [ + "Object", + "MouseOnlyCursorX(Object.Layer(), 0)", + "MouseOnlyCursorY(Object.Layer(), 0)" + ] + } + ], + "actions": [ + { + "type": { + "value": "SetBooleanVariable" + }, + "parameters": [ + "MouseIsInside", + "True", + "" + ] + } + ] + }, + { + "type": "BuiltinCommonInstructions::Comment", + "color": { + "b": 109, + "g": 230, + "r": 255, + "textB": 0, + "textG": 0, + "textR": 0 + }, + "comment": "Touches are always pressed, so ShouldCheckHovering doesn't matter." + }, + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [], + "actions": [ + { + "type": { + "value": "SetBooleanVariable" + }, + "parameters": [ + "TouchIsInside", + "False", + "" + ] + } + ] + }, + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [ + { + "type": { + "value": "NumberVariable" + }, + "parameters": [ + "TouchId", + "!=", + "0" + ] + }, + { + "type": { + "value": "CollisionPoint" + }, + "parameters": [ + "Object", + "TouchX(TouchId, Object.Layer(), 0)", + "TouchY(TouchId, Object.Layer(), 0)" + ] + } + ], + "actions": [ + { + "type": { + "value": "SetBooleanVariable" + }, + "parameters": [ + "TouchIsInside", + "True", + "" + ] + } + ] + } + ], + "parameters": [] + }, + { + "colorB": 228, + "colorG": 176, + "colorR": 74, + "creationTime": 0, + "name": "Handle touch start", + "source": "", + "type": "BuiltinCommonInstructions::Group", + "events": [ + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [ + { + "type": { + "value": "HasAnyTouchOrMouseStarted" + }, + "parameters": [ + "" + ] + } + ], + "actions": [ + { + "type": { + "value": "SetNumberVariable" + }, + "parameters": [ + "Index", + "=", + "0" + ] + } + ], + "events": [ + { + "type": "BuiltinCommonInstructions::Repeat", + "repeatExpression": "StartedTouchOrMouseCount()", + "conditions": [], + "actions": [], + "events": [ + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [ + { + "type": { + "value": "CollisionPoint" + }, + "parameters": [ + "Object", + "TouchX(StartedTouchOrMouseId(Index), Object.Layer(), 0)", + "TouchY(StartedTouchOrMouseId(Index), Object.Layer(), 0)" + ] + } + ], + "actions": [ + { + "type": { + "value": "SetNumberVariable" + }, + "parameters": [ + "TouchId", + "=", + "StartedTouchOrMouseId(Index)" + ] + }, + { + "type": { + "value": "SetBooleanVariable" + }, + "parameters": [ + "TouchIsInside", + "True", + "" + ] + } + ], + "events": [ + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [ + { + "type": { + "value": "BuiltinCommonInstructions::Or" + }, + "parameters": [], + "subInstructions": [ + { + "type": { + "value": "StringVariable" + }, + "parameters": [ + "State", + "=", + "\"Hovered\"" + ] + }, + { + "type": { + "value": "StringVariable" + }, + "parameters": [ + "State", + "=", + "\"Idle\"" + ] + } + ] + } + ], + "actions": [ + { + "type": { + "value": "SetStringVariable" + }, + "parameters": [ + "State", + "=", + "\"PressedInside\"" + ] + } + ] + } + ] + }, + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [], + "actions": [ + { + "type": { + "value": "SetNumberVariable" + }, + "parameters": [ + "Index", + "+", + "1" + ] + } + ] + } + ] + } + ] + } + ], + "parameters": [] + }, + { + "colorB": 228, + "colorG": 176, + "colorR": 74, + "creationTime": 0, + "name": "Apply position changes", + "source": "", + "type": "BuiltinCommonInstructions::Group", + "events": [ + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [ + { + "type": { + "inverted": true, + "value": "BooleanVariable" + }, + "parameters": [ + "MouseIsInside", + "True", + "" + ] + }, + { + "type": { + "value": "StringVariable" + }, + "parameters": [ + "State", + "=", + "\"Hovered\"" + ] + } + ], + "actions": [ + { + "type": { + "value": "SetStringVariable" + }, + "parameters": [ + "State", + "=", + "\"Idle\"" + ] + } + ] + }, + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [ + { + "type": { + "value": "BooleanVariable" + }, + "parameters": [ + "MouseIsInside", + "True", + "" + ] + }, + { + "type": { + "value": "StringVariable" + }, + "parameters": [ + "State", + "=", + "\"Idle\"" + ] + } + ], + "actions": [ + { + "type": { + "value": "SetStringVariable" + }, + "parameters": [ + "State", + "=", + "\"Hovered\"" + ] + } + ] + }, + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [ + { + "type": { + "inverted": true, + "value": "BooleanVariable" + }, + "parameters": [ + "TouchIsInside", + "True", + "" + ] + }, + { + "type": { + "value": "StringVariable" + }, + "parameters": [ + "State", + "=", + "\"PressedInside\"" + ] + } + ], + "actions": [ + { + "type": { + "value": "SetStringVariable" + }, + "parameters": [ + "State", + "=", + "\"PressedOutside\"" + ] + } + ] + }, + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [ + { + "type": { + "value": "BooleanVariable" + }, + "parameters": [ + "TouchIsInside", + "True", + "" + ] + }, + { + "type": { + "value": "StringVariable" + }, + "parameters": [ + "State", + "=", + "\"PressedOutside\"" + ] + } + ], + "actions": [ + { + "type": { + "value": "SetStringVariable" + }, + "parameters": [ + "State", + "=", + "\"PressedInside\"" + ] + } + ] + } + ], + "parameters": [] + }, + { + "colorB": 228, + "colorG": 176, + "colorR": 74, + "creationTime": 0, + "name": "Handle touch end", + "source": "", + "type": "BuiltinCommonInstructions::Group", + "events": [ + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [ + { + "type": { + "value": "HasTouchEnded" + }, + "parameters": [ + "", + "TouchId" + ] + } + ], + "actions": [ + { + "type": { + "value": "SetNumberVariable" + }, + "parameters": [ + "TouchId", + "=", + "0" + ] + } + ], + "events": [ + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [ + { + "type": { + "value": "StringVariable" + }, + "parameters": [ + "State", + "=", + "\"PressedInside\"" + ] + } + ], + "actions": [ + { + "type": { + "value": "SetStringVariable" + }, + "parameters": [ + "State", + "=", + "\"Validated\"" + ] + } + ] + }, + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [ + { + "type": { + "inverted": true, + "value": "StringVariable" + }, + "parameters": [ + "State", + "=", + "\"PressedInside\"" + ] + }, + { + "type": { + "inverted": true, + "value": "StringVariable" + }, + "parameters": [ + "State", + "=", + "\"Validated\"" + ] + } + ], + "actions": [ + { + "type": { + "value": "SetStringVariable" + }, + "parameters": [ + "State", + "=", + "\"Idle\"" + ] + } + ] + } + ] + } + ], + "parameters": [] + } + ], + "parameters": [] + } + ], + "parameters": [ + { + "description": "Object", + "name": "Object", + "type": "object" + }, + { + "description": "Behavior", + "name": "Behavior", + "supplementaryInformation": "ButtonStates::ButtonFSM", + "type": "behavior" + } + ], + "objectGroups": [] + }, + { + "fullName": "", + "functionType": "Action", + "name": "onDeActivate", + "sentence": "", + "events": [ + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [], + "actions": [ + { + "type": { + "value": "ButtonStates::ButtonFSM::ResetState" + }, + "parameters": [ + "Object", + "Behavior", + "" + ] + } + ] + } + ], + "parameters": [ + { + "description": "Object", + "name": "Object", + "type": "object" + }, + { + "description": "Behavior", + "name": "Behavior", + "supplementaryInformation": "ButtonStates::ButtonFSM", + "type": "behavior" + } + ], + "objectGroups": [] + }, + { + "description": "Reset the state of the button.", + "fullName": "Reset state", + "functionType": "Action", + "name": "ResetState", + "private": true, + "sentence": "Reset the button state of _PARAM0_", + "events": [ + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [], + "actions": [ + { + "type": { + "value": "SetStringVariable" + }, + "parameters": [ + "State", + "=", + "\"Idle\"" + ] + }, + { + "type": { + "value": "SetNumberVariable" + }, + "parameters": [ + "TouchId", + "=", + "0" + ] + } + ] + } + ], + "parameters": [ + { + "description": "Object", + "name": "Object", + "type": "object" + }, + { + "description": "Behavior", + "name": "Behavior", + "supplementaryInformation": "ButtonStates::ButtonFSM", + "type": "behavior" + } + ], + "objectGroups": [] + }, + { + "description": "Check if the button is not used.", + "fullName": "Is idle", + "functionType": "Condition", + "name": "IsIdle", + "sentence": "_PARAM0_ is idle", + "events": [ + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [ + { + "type": { + "value": "StringVariable" + }, + "parameters": [ + "State", + "=", + "\"Idle\"" + ] + } + ], + "actions": [ + { + "type": { + "value": "SetReturnBoolean" + }, + "parameters": [ + "True" + ] + } + ] + } + ], + "parameters": [ + { + "description": "Object", + "name": "Object", + "type": "object" + }, + { + "description": "Behavior", + "name": "Behavior", + "supplementaryInformation": "ButtonStates::ButtonFSM", + "type": "behavior" + } + ], + "objectGroups": [] + }, + { + "description": "Check if the button was just clicked.", + "fullName": "Is clicked", + "functionType": "Condition", + "name": "IsClicked", + "sentence": "_PARAM0_ is clicked", + "events": [ + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [ + { + "type": { + "value": "StringVariable" + }, + "parameters": [ + "State", + "=", + "\"Validated\"" + ] + } + ], + "actions": [ + { + "type": { + "value": "SetReturnBoolean" + }, + "parameters": [ + "True" + ] + } + ] + } + ], + "parameters": [ + { + "description": "Object", + "name": "Object", + "type": "object" + }, + { + "description": "Behavior", + "name": "Behavior", + "supplementaryInformation": "ButtonStates::ButtonFSM", + "type": "behavior" + } + ], + "objectGroups": [] + }, + { + "description": "Check if the cursor is hovered over the button.", + "fullName": "Is hovered", + "functionType": "Condition", + "name": "IsHovered", + "sentence": "_PARAM0_ is hovered", + "events": [ + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [ + { + "type": { + "value": "StringVariable" + }, + "parameters": [ + "State", + "=", + "\"Hovered\"" + ] + } + ], + "actions": [ + { + "type": { + "value": "SetReturnBoolean" + }, + "parameters": [ + "True" + ] + } + ] + } + ], + "parameters": [ + { + "description": "Object", + "name": "Object", + "type": "object" + }, + { + "description": "Behavior", + "name": "Behavior", + "supplementaryInformation": "ButtonStates::ButtonFSM", + "type": "behavior" + } + ], + "objectGroups": [] + }, + { + "description": "Check if the button is either hovered or pressed but not hovered.", + "fullName": "Is focused", + "functionType": "Condition", + "name": "IsFocused", + "sentence": "_PARAM0_ is focused", + "events": [ + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [ + { + "type": { + "value": "StringVariable" + }, + "parameters": [ + "State", + "=", + "\"Hovered\"" + ] + } + ], + "actions": [ + { + "type": { + "value": "SetReturnBoolean" + }, + "parameters": [ + "True" + ] + } + ] + }, + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [ + { + "type": { + "value": "StringVariable" + }, + "parameters": [ + "State", + "=", + "\"PressedOutside\"" + ] + } + ], + "actions": [ + { + "type": { + "value": "SetReturnBoolean" + }, + "parameters": [ + "True" + ] + } + ] + } + ], + "parameters": [ + { + "description": "Object", + "name": "Object", + "type": "object" + }, + { + "description": "Behavior", + "name": "Behavior", + "supplementaryInformation": "ButtonStates::ButtonFSM", + "type": "behavior" + } + ], + "objectGroups": [] + }, + { + "description": "Check if the button is currently being pressed with mouse or touch.", + "fullName": "Is pressed", + "functionType": "Condition", + "name": "IsPressed", + "sentence": "_PARAM0_ is pressed", + "events": [ + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [ + { + "type": { + "value": "StringVariable" + }, + "parameters": [ + "State", + "=", + "\"PressedInside\"" + ] + } + ], + "actions": [ + { + "type": { + "value": "SetReturnBoolean" + }, + "parameters": [ + "True" + ] + } + ] + } + ], + "parameters": [ + { + "description": "Object", + "name": "Object", + "type": "object" + }, + { + "description": "Behavior", + "name": "Behavior", + "supplementaryInformation": "ButtonStates::ButtonFSM", + "type": "behavior" + } + ], + "objectGroups": [] + }, + { + "description": "Check if the button is currently being pressed outside with mouse or touch.", + "fullName": "Is held outside", + "functionType": "Condition", + "name": "IsPressedOutside", + "sentence": "_PARAM0_ is held outside", + "events": [ + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [ + { + "type": { + "value": "StringVariable" + }, + "parameters": [ + "State", + "=", + "\"PressedOutside\"" + ] + } + ], + "actions": [ + { + "type": { + "value": "SetReturnBoolean" + }, + "parameters": [ + "True" + ] + } + ] + } + ], + "parameters": [ + { + "description": "Object", + "name": "Object", + "type": "object" + }, + { + "description": "Behavior", + "name": "Behavior", + "supplementaryInformation": "ButtonStates::ButtonFSM", + "type": "behavior" + } + ], + "objectGroups": [] + }, + { + "description": "the touch id that is using the button or 0 if none.", + "fullName": "Touch id", + "functionType": "ExpressionAndCondition", + "name": "TouchId", + "sentence": "the touch id", + "events": [ + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [], + "actions": [ + { + "type": { + "value": "SetReturnNumber" + }, + "parameters": [ + "TouchId" + ] + } + ] + } + ], + "expressionType": { + "type": "expression" + }, + "parameters": [ + { + "description": "Object", + "name": "Object", + "type": "object" + }, + { + "description": "Behavior", + "name": "Behavior", + "supplementaryInformation": "ButtonStates::ButtonFSM", + "type": "behavior" + } + ], + "objectGroups": [] + } + ], + "eventsFunctionsFolderStructure": { + "folderName": "__ROOT", + "children": [ + { + "functionName": "doStepPreEvents" + }, + { + "functionName": "onDeActivate" + }, + { + "functionName": "ResetState" + }, + { + "functionName": "IsIdle" + }, + { + "functionName": "IsClicked" + }, + { + "functionName": "IsHovered" + }, + { + "functionName": "IsFocused" + }, + { + "functionName": "IsPressed" + }, + { + "functionName": "IsPressedOutside" + }, + { + "functionName": "TouchId" + } + ] + }, + "propertyDescriptors": [ + { + "value": "true", + "type": "Boolean", + "label": "", + "description": "Should check hovering", + "hidden": true, + "name": "ShouldCheckHovering" + }, + { + "value": "Idle", + "type": "Choice", + "label": "State", + "choices": [ + { + "label": "Idle", + "value": "Idle" + }, + { + "label": "Hovered", + "value": "Hovered" + }, + { + "label": "PressedInside", + "value": "PressedInside" + }, + { + "label": "PressedOutside", + "value": "PressedOutside" + }, + { + "label": "Validated", + "value": "Validated" + } + ], + "hidden": true, + "name": "State" + }, + { + "value": "0", + "type": "Number", + "label": "Touch id", + "hidden": true, + "name": "TouchId" + }, + { + "value": "", + "type": "Boolean", + "label": "Touch is inside", + "hidden": true, + "name": "TouchIsInside" + }, + { + "value": "", + "type": "Boolean", + "label": "Mouse is inside", + "hidden": true, + "name": "MouseIsInside" + }, + { + "value": "", + "type": "Number", + "label": "", + "hidden": true, + "name": "Index" + } + ], + "propertiesFolderStructure": { + "folderName": "__ROOT", + "children": [ + { + "propertyName": "ShouldCheckHovering" + }, + { + "propertyName": "State" + }, + { + "propertyName": "TouchId" + }, + { + "propertyName": "TouchIsInside" + }, + { + "propertyName": "MouseIsInside" + }, + { + "propertyName": "Index" + } + ] + } + }, + { + "description": "Enable effects on buttons based on their state.", + "fullName": "Button object effects", + "helpPath": "", + "iconUrl": "", + "name": "ButtonObjectEffects", + "objectType": "", + "previewIconUrl": "", + "eventsFunctions": [ + { + "fullName": "", + "functionType": "Action", + "name": "onCreated", + "sentence": "", + "events": [ + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [], + "actions": [ + { + "type": { + "value": "EffectCapability::EffectBehavior::EnableEffect" + }, + "parameters": [ + "Object", + "Effect", + "IdleEffect", + "yes" + ] + }, + { + "type": { + "value": "EffectCapability::EffectBehavior::EnableEffect" + }, + "parameters": [ + "Object", + "Effect", + "FocusedEffect", + "no" + ] + }, + { + "type": { + "value": "EffectCapability::EffectBehavior::EnableEffect" + }, + "parameters": [ + "Object", + "Effect", + "PressedEffect", + "no" + ] + } + ] + } + ], + "parameters": [ + { + "description": "Object", + "name": "Object", + "type": "object" + }, + { + "description": "Behavior", + "name": "Behavior", + "supplementaryInformation": "ButtonStates::ButtonObjectEffects", + "type": "behavior" + } + ], + "objectGroups": [] + }, + { + "fullName": "", + "functionType": "Action", + "name": "doStepPreEvents", + "sentence": "", + "events": [ + { + "colorB": 228, + "colorG": 176, + "colorR": 74, + "creationTime": 0, + "name": "States", + "source": "", + "type": "BuiltinCommonInstructions::Group", + "events": [ + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [], + "actions": [ + { + "type": { + "value": "EffectCapability::EffectBehavior::EnableEffect" + }, + "parameters": [ + "Object", + "Effect", + "IdleEffect", + "no" + ] + }, + { + "type": { + "value": "EffectCapability::EffectBehavior::EnableEffect" + }, + "parameters": [ + "Object", + "Effect", + "FocusedEffect", + "no" + ] + }, + { + "type": { + "value": "EffectCapability::EffectBehavior::EnableEffect" + }, + "parameters": [ + "Object", + "Effect", + "PressedEffect", + "no" + ] + } + ] + }, + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [ + { + "type": { + "value": "StringVariable" + }, + "parameters": [ + "IdleEffect", + "!=", + "\"\"" + ] + }, + { + "type": { + "value": "ButtonStates::ButtonFSM::IsIdle" + }, + "parameters": [ + "Object", + "ButtonFSM", + "" + ] + } + ], + "actions": [ + { + "type": { + "value": "EffectCapability::EffectBehavior::EnableEffect" + }, + "parameters": [ + "Object", + "Effect", + "IdleEffect", + "yes" + ] + } + ] + }, + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [ + { + "type": { + "value": "StringVariable" + }, + "parameters": [ + "FocusedEffect", + "!=", + "\"\"" + ] + }, + { + "type": { + "value": "ButtonStates::ButtonFSM::IsFocused" + }, + "parameters": [ + "Object", + "ButtonFSM", + "" + ] + } + ], + "actions": [ + { + "type": { + "value": "EffectCapability::EffectBehavior::EnableEffect" + }, + "parameters": [ + "Object", + "Effect", + "FocusedEffect", + "yes" + ] + } + ] + }, + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [ + { + "type": { + "value": "StringVariable" + }, + "parameters": [ + "PressedEffect", + "!=", + "\"\"" + ] + }, + { + "type": { + "value": "ButtonStates::ButtonFSM::IsPressed" + }, + "parameters": [ + "Object", + "ButtonFSM", + "" + ] + } + ], + "actions": [ + { + "type": { + "value": "EffectCapability::EffectBehavior::EnableEffect" + }, + "parameters": [ + "Object", + "Effect", + "PressedEffect", + "yes" + ] + } + ] + } + ], + "parameters": [] + } + ], + "parameters": [ + { + "description": "Object", + "name": "Object", + "type": "object" + }, + { + "description": "Behavior", + "name": "Behavior", + "supplementaryInformation": "ButtonStates::ButtonObjectEffects", + "type": "behavior" + } + ], + "objectGroups": [] + }, + { + "description": "the idle state effect of the object.", + "fullName": "Idle state effect", + "functionType": "ExpressionAndCondition", + "group": "Button object effects configuration", + "name": "IdleEffect", + "sentence": "the idle state effect", + "events": [ + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [], + "actions": [ + { + "type": { + "value": "SetReturnString" + }, + "parameters": [ + "IdleEffect" + ] + } + ] + } + ], + "expressionType": { + "type": "string" + }, + "parameters": [ + { + "description": "Object", + "name": "Object", + "type": "object" + }, + { + "description": "Behavior", + "name": "Behavior", + "supplementaryInformation": "ButtonStates::ButtonObjectEffects", + "type": "behavior" + } + ], + "objectGroups": [] + }, + { + "fullName": "", + "functionType": "ActionWithOperator", + "getterName": "IdleEffect", + "group": "Button object effects configuration", + "name": "SetIdleEffect", + "sentence": "", + "events": [ + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [], + "actions": [ + { + "type": { + "value": "SetStringVariable" + }, + "parameters": [ + "IdleEffect", + "=", + "Value" + ] + } + ] + } + ], + "parameters": [ + { + "description": "Object", + "name": "Object", + "type": "object" + }, + { + "description": "Behavior", + "name": "Behavior", + "supplementaryInformation": "ButtonStates::ButtonObjectEffects", + "type": "behavior" + } + ], + "objectGroups": [] + }, + { + "description": "the focused state effect of the object. The state is Focused when the button is hovered or held outside.", + "fullName": "Focused state effect", + "functionType": "ExpressionAndCondition", + "group": "Button object effects configuration", + "name": "FocusedEffect", + "sentence": "the focused state effect", + "events": [ + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [], + "actions": [ + { + "type": { + "value": "SetReturnString" + }, + "parameters": [ + "FocusedEffect" + ] + } + ] + } + ], + "expressionType": { + "type": "string" + }, + "parameters": [ + { + "description": "Object", + "name": "Object", + "type": "object" + }, + { + "description": "Behavior", + "name": "Behavior", + "supplementaryInformation": "ButtonStates::ButtonObjectEffects", + "type": "behavior" + } + ], + "objectGroups": [] + }, + { + "fullName": "", + "functionType": "ActionWithOperator", + "getterName": "FocusedEffect", + "group": "Button object effects configuration", + "name": "SetFocusedEffect", + "sentence": "", + "events": [ + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [], + "actions": [ + { + "type": { + "value": "SetStringVariable" + }, + "parameters": [ + "FocusedEffect", + "=", + "Value" + ] + } + ] + } + ], + "parameters": [ + { + "description": "Object", + "name": "Object", + "type": "object" + }, + { + "description": "Behavior", + "name": "Behavior", + "supplementaryInformation": "ButtonStates::ButtonObjectEffects", + "type": "behavior" + } + ], + "objectGroups": [] + }, + { + "description": "the pressed state effect of the object.", + "fullName": "Pressed state effect", + "functionType": "ExpressionAndCondition", + "group": "Button object effects configuration", + "name": "PressedEffect", + "sentence": "the pressed state effect", + "events": [ + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [], + "actions": [ + { + "type": { + "value": "SetReturnString" + }, + "parameters": [ + "PressedEffect" + ] + } + ] + } + ], + "expressionType": { + "type": "string" + }, + "parameters": [ + { + "description": "Object", + "name": "Object", + "type": "object" + }, + { + "description": "Behavior", + "name": "Behavior", + "supplementaryInformation": "ButtonStates::ButtonObjectEffects", + "type": "behavior" + } + ], + "objectGroups": [] + }, + { + "fullName": "", + "functionType": "ActionWithOperator", + "getterName": "PressedEffect", + "group": "Button object effects configuration", + "name": "SetPressedEffect", + "sentence": "", + "events": [ + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [], + "actions": [ + { + "type": { + "value": "SetStringVariable" + }, + "parameters": [ + "PressedEffect", + "=", + "Value" + ] + } + ] + } + ], + "parameters": [ + { + "description": "Object", + "name": "Object", + "type": "object" + }, + { + "description": "Behavior", + "name": "Behavior", + "supplementaryInformation": "ButtonStates::ButtonObjectEffects", + "type": "behavior" + } + ], + "objectGroups": [] + } + ], + "eventsFunctionsFolderStructure": { + "folderName": "__ROOT", + "children": [ + { + "functionName": "onCreated" + }, + { + "functionName": "doStepPreEvents" + }, + { + "folderName": "Button object effects configuration", + "children": [ + { + "functionName": "IdleEffect" + }, + { + "functionName": "SetIdleEffect" + }, + { + "functionName": "FocusedEffect" + }, + { + "functionName": "SetFocusedEffect" + }, + { + "functionName": "PressedEffect" + }, + { + "functionName": "SetPressedEffect" + } + ] + } + ] + }, + "propertyDescriptors": [ + { + "value": "", + "type": "Behavior", + "label": "Button states", + "extraInformation": [ + "ButtonStates::ButtonFSM" + ], + "choices": [], + "name": "ButtonFSM" + }, + { + "value": "", + "type": "Behavior", + "label": "Effect capability", + "extraInformation": [ + "EffectCapability::EffectBehavior" + ], + "choices": [], + "name": "Effect" + }, + { + "value": "", + "type": "String", + "label": "Idle state effect", + "group": "Effects", + "name": "IdleEffect" + }, + { + "value": "", + "type": "String", + "label": "Focused state effect", + "description": "The state is Focused when the button is hovered or held outside.", + "group": "Effects", + "name": "FocusedEffect" + }, + { + "value": "", + "type": "String", + "label": "Pressed state effect", + "group": "Effects", + "name": "PressedEffect" + } + ], + "propertiesFolderStructure": { + "folderName": "__ROOT", + "children": [ + { + "propertyName": "ButtonFSM" + }, + { + "propertyName": "Effect" + }, + { + "folderName": "Effects", + "children": [ + { + "propertyName": "IdleEffect" + }, + { + "propertyName": "FocusedEffect" + }, + { + "propertyName": "PressedEffect" + } + ] + } + ] + } + }, + { + "description": "Change the animation of buttons according to their state.", + "fullName": "Button animation", + "helpPath": "", + "iconUrl": "", + "name": "ButtonAnimationName", + "objectType": "", + "previewIconUrl": "", + "eventsFunctions": [ + { + "fullName": "", + "functionType": "Action", + "name": "onCreated", + "sentence": "", + "events": [ + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [], + "actions": [ + { + "type": { + "value": "AnimatableCapability::AnimatableBehavior::SetName" + }, + "parameters": [ + "Object", + "Animation", + "=", + "IdleAnimationName" + ] + } + ] + } + ], + "parameters": [ + { + "description": "Object", + "name": "Object", + "type": "object" + }, + { + "description": "Behavior", + "name": "Behavior", + "supplementaryInformation": "ButtonStates::ButtonAnimationName", + "type": "behavior" + } + ], + "objectGroups": [] + }, + { + "fullName": "", + "functionType": "Action", + "name": "doStepPreEvents", + "sentence": "", + "events": [ + { + "colorB": 228, + "colorG": 176, + "colorR": 74, + "creationTime": 0, + "name": "Change based on state", + "source": "", + "type": "BuiltinCommonInstructions::Group", + "events": [ + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [ + { + "type": { + "value": "ButtonStates::ButtonFSM::IsIdle" + }, + "parameters": [ + "Object", + "ButtonFSM", + "" + ] + } + ], + "actions": [ + { + "type": { + "value": "AnimatableCapability::AnimatableBehavior::SetName" + }, + "parameters": [ + "Object", + "Animation", + "=", + "IdleAnimationName" + ] + } + ] + }, + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [ + { + "type": { + "value": "ButtonStates::ButtonFSM::IsFocused" + }, + "parameters": [ + "Object", + "ButtonFSM", + "" + ] + } + ], + "actions": [ + { + "type": { + "value": "AnimatableCapability::AnimatableBehavior::SetName" + }, + "parameters": [ + "Object", + "Animation", + "=", + "FocusedAnimationName" + ] + } + ] + }, + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [ + { + "type": { + "value": "ButtonStates::ButtonFSM::IsPressed" + }, + "parameters": [ + "Object", + "ButtonFSM", + "" + ] + } + ], + "actions": [ + { + "type": { + "value": "AnimatableCapability::AnimatableBehavior::SetName" + }, + "parameters": [ + "Object", + "Animation", + "=", + "PressedAnimationName" + ] + } + ] + } + ], + "parameters": [] + } + ], + "parameters": [ + { + "description": "Object", + "name": "Object", + "type": "object" + }, + { + "description": "Behavior", + "name": "Behavior", + "supplementaryInformation": "ButtonStates::ButtonAnimationName", + "type": "behavior" + } + ], + "objectGroups": [] + }, + { + "description": "the idle state animation name of the object.", + "fullName": "Idle state animation name", + "functionType": "ExpressionAndCondition", + "group": "Button animation configuration", + "name": "IdleAnimationName", + "sentence": "the idle state animation name", + "events": [ + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [], + "actions": [ + { + "type": { + "value": "SetReturnString" + }, + "parameters": [ + "IdleAnimationName" + ] + } + ] + } + ], + "expressionType": { + "type": "string" + }, + "parameters": [ + { + "description": "Object", + "name": "Object", + "type": "object" + }, + { + "description": "Behavior", + "name": "Behavior", + "supplementaryInformation": "ButtonStates::ButtonAnimationName", + "type": "behavior" + } + ], + "objectGroups": [] + }, + { + "fullName": "", + "functionType": "ActionWithOperator", + "getterName": "IdleAnimationName", + "group": "Button animation configuration", + "name": "SetIdleAnimationName", + "sentence": "", + "events": [ + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [], + "actions": [ + { + "type": { + "value": "SetStringVariable" + }, + "parameters": [ + "IdleAnimationName", + "=", + "Value" + ] + } + ] + } + ], + "parameters": [ + { + "description": "Object", + "name": "Object", + "type": "object" + }, + { + "description": "Behavior", + "name": "Behavior", + "supplementaryInformation": "ButtonStates::ButtonAnimationName", + "type": "behavior" + } + ], + "objectGroups": [] + }, + { + "description": "the focused state animation name of the object. The state is Focused when the button is hovered or held outside.", + "fullName": "Focused state animation name", + "functionType": "ExpressionAndCondition", + "group": "Button animation configuration", + "name": "FocusedAnimationName", + "sentence": "the focused state animation name", + "events": [ + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [], + "actions": [ + { + "type": { + "value": "SetReturnString" + }, + "parameters": [ + "FocusedAnimationName" + ] + } + ] + } + ], + "expressionType": { + "type": "string" + }, + "parameters": [ + { + "description": "Object", + "name": "Object", + "type": "object" + }, + { + "description": "Behavior", + "name": "Behavior", + "supplementaryInformation": "ButtonStates::ButtonAnimationName", + "type": "behavior" + } + ], + "objectGroups": [] + }, + { + "fullName": "", + "functionType": "ActionWithOperator", + "getterName": "FocusedAnimationName", + "group": "Button animation configuration", + "name": "SetFocusedAnimationName", + "sentence": "", + "events": [ + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [], + "actions": [ + { + "type": { + "value": "SetStringVariable" + }, + "parameters": [ + "FocusedAnimationName", + "=", + "Value" + ] + } + ] + } + ], + "parameters": [ + { + "description": "Object", + "name": "Object", + "type": "object" + }, + { + "description": "Behavior", + "name": "Behavior", + "supplementaryInformation": "ButtonStates::ButtonAnimationName", + "type": "behavior" + } + ], + "objectGroups": [] + }, + { + "description": "the pressed state animation name of the object.", + "fullName": "Pressed state animation name", + "functionType": "ExpressionAndCondition", + "group": "Button animation configuration", + "name": "PressedAnimationName", + "sentence": "the pressed state animation name", + "events": [ + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [], + "actions": [ + { + "type": { + "value": "SetReturnString" + }, + "parameters": [ + "PressedAnimationName" + ] + } + ] + } + ], + "expressionType": { + "type": "string" + }, + "parameters": [ + { + "description": "Object", + "name": "Object", + "type": "object" + }, + { + "description": "Behavior", + "name": "Behavior", + "supplementaryInformation": "ButtonStates::ButtonAnimationName", + "type": "behavior" + } + ], + "objectGroups": [] + }, + { + "fullName": "", + "functionType": "ActionWithOperator", + "getterName": "PressedAnimationName", + "group": "Button animation configuration", + "name": "SetPressedAnimationName", + "sentence": "", + "events": [ + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [], + "actions": [ + { + "type": { + "value": "SetStringVariable" + }, + "parameters": [ + "PressedAnimationName", + "=", + "Value" + ] + } + ] + } + ], + "parameters": [ + { + "description": "Object", + "name": "Object", + "type": "object" + }, + { + "description": "Behavior", + "name": "Behavior", + "supplementaryInformation": "ButtonStates::ButtonAnimationName", + "type": "behavior" + } + ], + "objectGroups": [] + } + ], + "eventsFunctionsFolderStructure": { + "folderName": "__ROOT", + "children": [ + { + "functionName": "onCreated" + }, + { + "functionName": "doStepPreEvents" + }, + { + "folderName": "Button animation configuration", + "children": [ + { + "functionName": "IdleAnimationName" + }, + { + "functionName": "SetIdleAnimationName" + }, + { + "functionName": "FocusedAnimationName" + }, + { + "functionName": "SetFocusedAnimationName" + }, + { + "functionName": "PressedAnimationName" + }, + { + "functionName": "SetPressedAnimationName" + } + ] + } + ] + }, + "propertyDescriptors": [ + { + "value": "", + "type": "Behavior", + "label": "Animatable capability", + "extraInformation": [ + "AnimatableCapability::AnimatableBehavior" + ], + "choices": [], + "name": "Animation" + }, + { + "value": "", + "type": "Behavior", + "label": "Button states", + "extraInformation": [ + "ButtonStates::ButtonFSM" + ], + "choices": [], + "name": "ButtonFSM" + }, + { + "value": "Idle", + "type": "String", + "label": "Idle state animation name", + "group": "Animation", + "name": "IdleAnimationName" + }, + { + "value": "Focused", + "type": "String", + "label": "Focused state animation name", + "description": "The state is Focused when the button is hovered or held outside.", + "group": "Animation", + "name": "FocusedAnimationName" + }, + { + "value": "Pressed", + "type": "String", + "label": "Pressed state animation name", + "group": "Animation", + "name": "PressedAnimationName" + } + ], + "propertiesFolderStructure": { + "folderName": "__ROOT", + "children": [ + { + "propertyName": "Animation" + }, + { + "propertyName": "ButtonFSM" + }, + { + "folderName": "Animation", + "children": [ + { + "propertyName": "IdleAnimationName" + }, + { + "propertyName": "FocusedAnimationName" + }, + { + "propertyName": "PressedAnimationName" + } + ] + } + ] + } + }, + { + "description": "Smoothly change an effect on buttons according to their state.", + "fullName": "Button object effect tween", + "helpPath": "", + "iconUrl": "", + "name": "ButtonObjectEffectTween", + "objectType": "", + "previewIconUrl": "", + "eventsFunctions": [ + { + "fullName": "", + "functionType": "Action", + "name": "onCreated", + "sentence": "", + "events": [ + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [], + "actions": [ + { + "type": { + "value": "SetNumberVariable" + }, + "parameters": [ + "EffectValue", + "=", + "IdleValue" + ] + } + ] + } + ], + "parameters": [ + { + "description": "Object", + "name": "Object", + "type": "object" + }, + { + "description": "Behavior", + "name": "Behavior", + "supplementaryInformation": "ButtonStates::ButtonObjectEffectTween", + "type": "behavior" + } + ], + "objectGroups": [] + }, + { + "fullName": "", + "functionType": "Action", + "name": "doStepPreEvents", + "sentence": "", + "events": [ + { + "colorB": 228, + "colorG": 176, + "colorR": 74, + "creationTime": 0, + "name": "States", + "source": "", + "type": "BuiltinCommonInstructions::Group", + "events": [ + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [ + { + "type": { + "value": "ButtonStates::ButtonFSM::IsIdle" + }, + "parameters": [ + "Object", + "ButtonFSM", + "" + ] + }, + { + "type": { + "value": "StringVariable" + }, + "parameters": [ + "PreviousState", + "!=", + "\"Idle\"" + ] + } + ], + "actions": [ + { + "type": { + "value": "SetStringVariable" + }, + "parameters": [ + "PreviousState", + "=", + "\"Idle\"" + ] + }, + { + "type": { + "value": "ButtonStates::ButtonObjectEffectTween::FadeOut" + }, + "parameters": [ + "Object", + "Behavior", + "IdleValue", + "" + ] + } + ] + }, + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [ + { + "type": { + "value": "ButtonStates::ButtonFSM::IsFocused" + }, + "parameters": [ + "Object", + "ButtonFSM", + "" + ] + } + ], + "actions": [], + "events": [ + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [ + { + "type": { + "value": "StringVariable" + }, + "parameters": [ + "PreviousState", + "=", + "\"Idle\"" + ] + } + ], + "actions": [ + { + "type": { + "value": "SetStringVariable" + }, + "parameters": [ + "PreviousState", + "=", + "\"Focused\"" + ] + }, + { + "type": { + "value": "ButtonStates::ButtonObjectEffectTween::FadeIn" + }, + "parameters": [ + "Object", + "Behavior", + "FocusedValue", + "" + ] + } + ] + }, + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [ + { + "type": { + "value": "StringVariable" + }, + "parameters": [ + "PreviousState", + "=", + "\"Pressed\"" + ] + } + ], + "actions": [ + { + "type": { + "value": "SetStringVariable" + }, + "parameters": [ + "PreviousState", + "=", + "\"Focused\"" + ] + }, + { + "type": { + "value": "ButtonStates::ButtonObjectEffectTween::FadeOut" + }, + "parameters": [ + "Object", + "Behavior", + "FocusedValue", + "" + ] + } + ] + } + ] + }, + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [ + { + "type": { + "value": "ButtonStates::ButtonFSM::IsPressed" + }, + "parameters": [ + "Object", + "ButtonFSM", + "" + ] + }, + { + "type": { + "value": "StringVariable" + }, + "parameters": [ + "PreviousState", + "!=", + "\"Pressed\"" + ] + } + ], + "actions": [ + { + "type": { + "value": "SetStringVariable" + }, + "parameters": [ + "PreviousState", + "=", + "\"Pressed\"" + ] + }, + { + "type": { + "value": "ButtonStates::ButtonObjectEffectTween::FadeIn" + }, + "parameters": [ + "Object", + "Behavior", + "PressedValue", + "" + ] + } + ] + } + ], + "parameters": [] + }, + { + "colorB": 228, + "colorG": 176, + "colorR": 74, + "creationTime": 0, + "name": "Tween", + "source": "", + "type": "BuiltinCommonInstructions::Group", + "events": [ + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [ + { + "type": { + "value": "StringVariable" + }, + "parameters": [ + "TweenState", + "=", + "\"FadeIn\"" + ] + } + ], + "actions": [ + { + "type": { + "value": "ButtonStates::ButtonObjectEffectTween::PlayTween" + }, + "parameters": [ + "Object", + "Behavior", + "FadeInDuration", + "FadeInEasing", + "" + ] + } + ] + }, + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [ + { + "type": { + "value": "StringVariable" + }, + "parameters": [ + "TweenState", + "=", + "\"FadeOut\"" + ] + } + ], + "actions": [ + { + "type": { + "value": "ButtonStates::ButtonObjectEffectTween::PlayTween" + }, + "parameters": [ + "Object", + "Behavior", + "FadeOutDuration", + "FadeOutEasing", + "" + ] + } + ] + } + ], + "parameters": [] + }, + { + "colorB": 228, + "colorG": 176, + "colorR": 74, + "creationTime": 0, + "name": "Toggle effect", + "source": "", + "type": "BuiltinCommonInstructions::Group", + "events": [ + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [ + { + "type": { + "value": "BooleanVariable" + }, + "parameters": [ + "ShouldDisableEffectInIdle", + "True", + "" + ] + } + ], + "actions": [], + "events": [ + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [ + { + "type": { + "value": "ButtonStates::ButtonFSM::IsIdle" + }, + "parameters": [ + "Object", + "ButtonFSM", + "" + ] + }, + { + "type": { + "value": "StringVariable" + }, + "parameters": [ + "TweenState", + "=", + "\"NoTween\"" + ] + } + ], + "actions": [ + { + "type": { + "value": "EffectCapability::EffectBehavior::EnableEffect" + }, + "parameters": [ + "Object", + "Effect", + "EffectName", + "no" + ] + } + ] + }, + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [ + { + "type": { + "inverted": true, + "value": "ButtonStates::ButtonFSM::IsIdle" + }, + "parameters": [ + "Object", + "ButtonFSM", + "" + ] + } + ], + "actions": [ + { + "type": { + "value": "EffectCapability::EffectBehavior::EnableEffect" + }, + "parameters": [ + "Object", + "Effect", + "EffectName", + "yes" + ] + } + ] + } + ] + } + ], + "parameters": [] + } + ], + "parameters": [ + { + "description": "Object", + "name": "Object", + "type": "object" + }, + { + "description": "Behavior", + "name": "Behavior", + "supplementaryInformation": "ButtonStates::ButtonObjectEffectTween", + "type": "behavior" + } + ], + "objectGroups": [] + }, + { + "fullName": "Time delta", + "functionType": "Expression", + "name": "TimeDelta", + "private": true, + "sentence": "", + "events": [ + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [], + "actions": [ + { + "type": { + "value": "SetReturnNumber" + }, + "parameters": [ + "TimeDelta() * LayerTimeScale(Object.Layer())" + ] + } + ] + } + ], + "expressionType": { + "type": "expression" + }, + "parameters": [ + { + "description": "Object", + "name": "Object", + "type": "object" + }, + { + "description": "Behavior", + "name": "Behavior", + "supplementaryInformation": "ButtonStates::ButtonObjectEffectTween", + "type": "behavior" + } + ], + "objectGroups": [] + }, + { + "fullName": "Fade in", + "functionType": "Action", + "name": "FadeIn", + "private": true, + "sentence": "_PARAM0_ fade in to _PARAM2_", + "events": [ + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [], + "actions": [ + { + "type": { + "value": "SetNumberVariable" + }, + "parameters": [ + "TweenTime", + "=", + "0" + ] + }, + { + "type": { + "value": "SetStringVariable" + }, + "parameters": [ + "TweenState", + "=", + "\"FadeIn\"" + ] + }, + { + "type": { + "value": "SetNumberVariable" + }, + "parameters": [ + "TweenInitialValue", + "=", + "EffectValue" + ] + }, + { + "type": { + "value": "SetNumberVariable" + }, + "parameters": [ + "TweenTargetedValue", + "=", + "Value" + ] + } + ] + } + ], + "parameters": [ + { + "description": "Object", + "name": "Object", + "type": "object" + }, + { + "description": "Behavior", + "name": "Behavior", + "supplementaryInformation": "ButtonStates::ButtonObjectEffectTween", + "type": "behavior" + }, + { + "description": "Value", + "name": "Value", + "type": "expression" + } + ], + "objectGroups": [] + }, + { + "fullName": "Fade out", + "functionType": "Action", + "name": "FadeOut", + "private": true, + "sentence": "_PARAM0_ fade out to _PARAM2_", + "events": [ + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [], + "actions": [ + { + "type": { + "value": "SetNumberVariable" + }, + "parameters": [ + "TweenTime", + "=", + "0" + ] + }, + { + "type": { + "value": "SetStringVariable" + }, + "parameters": [ + "TweenState", + "=", + "\"FadeOut\"" + ] + }, + { + "type": { + "value": "SetNumberVariable" + }, + "parameters": [ + "TweenInitialValue", + "=", + "EffectValue" + ] + }, + { + "type": { + "value": "SetNumberVariable" + }, + "parameters": [ + "TweenTargetedValue", + "=", + "Value" + ] + } + ] + } + ], + "parameters": [ + { + "description": "Object", + "name": "Object", + "type": "object" + }, + { + "description": "Behavior", + "name": "Behavior", + "supplementaryInformation": "ButtonStates::ButtonObjectEffectTween", + "type": "behavior" + }, + { + "description": "Value", + "name": "Value", + "type": "expression" + } + ], + "objectGroups": [] + }, + { + "fullName": "Play tween", + "functionType": "Action", + "name": "PlayTween", + "private": true, + "sentence": "Tween the effect property of object _PARAM0_ over _PARAM2_ seconds with _PARAM3_ easing", + "events": [ + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [ + { + "type": { + "value": "NumberVariable" + }, + "parameters": [ + "TweenTime", + "<", + "Duration" + ] + } + ], + "actions": [ + { + "type": { + "value": "SetNumberVariable" + }, + "parameters": [ + "TweenTime", + "+", + "Object.Behavior::TimeDelta()" + ] + }, + { + "type": { + "value": "SetNumberVariable" + }, + "parameters": [ + "EffectValue", + "=", + "Tween::Ease(Easing, TweenInitialValue, TweenTargetedValue, TweenTime / Duration)" + ] + } + ] + }, + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [ + { + "type": { + "value": "NumberVariable" + }, + "parameters": [ + "TweenTime", + ">=", + "Duration" + ] + } + ], + "actions": [ + { + "type": { + "value": "SetStringVariable" + }, + "parameters": [ + "TweenState", + "=", + "\"NoTween\"" + ] + }, + { + "type": { + "value": "SetNumberVariable" + }, + "parameters": [ + "EffectValue", + "=", + "TweenTargetedValue" + ] + } + ] + }, + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [], + "actions": [ + { + "type": { + "value": "EffectCapability::EffectBehavior::SetEffectDoubleParameter" + }, + "parameters": [ + "Object", + "Effect", + "EffectName", + "EffectProperty", + "EffectValue" + ] + } + ] + } + ], + "parameters": [ + { + "description": "Object", + "name": "Object", + "type": "object" + }, + { + "description": "Behavior", + "name": "Behavior", + "supplementaryInformation": "ButtonStates::ButtonObjectEffectTween", + "type": "behavior" + }, + { + "description": "Duration (in seconds)", + "name": "Duration", + "type": "expression" + }, + { + "description": "Easing", + "name": "Easing", + "supplementaryInformation": "[]", + "type": "string" + } + ], + "objectGroups": [] + }, + { + "description": "the effect name of the object.", + "fullName": "Effect name", + "functionType": "ExpressionAndCondition", + "group": "Button effect tween configuration", + "name": "EffectName", + "sentence": "the effect name", + "events": [ + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [], + "actions": [ + { + "type": { + "value": "SetReturnString" + }, + "parameters": [ + "EffectName" + ] + } + ] + } + ], + "expressionType": { + "type": "string" + }, + "parameters": [ + { + "description": "Object", + "name": "Object", + "type": "object" + }, + { + "description": "Behavior", + "name": "Behavior", + "supplementaryInformation": "ButtonStates::ButtonObjectEffectTween", + "type": "behavior" + } + ], + "objectGroups": [] + }, + { + "description": "the effect parameter of the object. The effect parameter names can be found in the effects tab with the \"Show parameter names\" action of the drop down menu.", + "fullName": "Effect parameter", + "functionType": "ExpressionAndCondition", + "group": "Button effect tween configuration", + "name": "EffectProperty", + "sentence": "the effect parameter", + "events": [ + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [], + "actions": [ + { + "type": { + "value": "SetReturnString" + }, + "parameters": [ + "EffectProperty" + ] + } + ] + } + ], + "expressionType": { + "type": "string" + }, + "parameters": [ + { + "description": "Object", + "name": "Object", + "type": "object" + }, + { + "description": "Behavior", + "name": "Behavior", + "supplementaryInformation": "ButtonStates::ButtonObjectEffectTween", + "type": "behavior" + } + ], + "objectGroups": [] + }, + { + "description": "Change the effect parameter of the object. The effect parameter names can be found in the effects tab with the \"Show parameter names\" action of the drop down menu.", + "fullName": "Effect parameter", + "functionType": "Action", + "getterName": "EffectName", + "group": "Button effect tween configuration", + "name": "SetEffectProperty", + "sentence": "Change the tweened effect of _PARAM0_ to _PARAM2_ with parameter _PARAM3_", + "events": [ + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [], + "actions": [ + { + "type": { + "value": "SetStringVariable" + }, + "parameters": [ + "EffectName", + "=", + "NewEffectName" + ] + }, + { + "type": { + "value": "SetStringVariable" + }, + "parameters": [ + "EffectProperty", + "=", + "NewPropertyName" + ] + } + ] + } + ], + "parameters": [ + { + "description": "Object", + "name": "Object", + "type": "object" + }, + { + "description": "Behavior", + "name": "Behavior", + "supplementaryInformation": "ButtonStates::ButtonObjectEffectTween", + "type": "behavior" + }, + { + "description": "Effect name", + "name": "NewEffectName", + "type": "string" + }, + { + "description": "Parameter name", + "name": "NewPropertyName", + "type": "string" + } + ], + "objectGroups": [] + }, + { + "description": "the idle effect parameter value of the object.", + "fullName": "Idle effect parameter value", + "functionType": "ExpressionAndCondition", + "group": "Button effect tween configuration", + "name": "IdleValue", + "sentence": "the idle effect parameter value", + "events": [ + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [], + "actions": [ + { + "type": { + "value": "SetReturnNumber" + }, + "parameters": [ + "IdleValue" + ] + } + ] + } + ], + "expressionType": { + "type": "expression" + }, + "parameters": [ + { + "description": "Object", + "name": "Object", + "type": "object" + }, + { + "description": "Behavior", + "name": "Behavior", + "supplementaryInformation": "ButtonStates::ButtonObjectEffectTween", + "type": "behavior" + } + ], + "objectGroups": [] + }, + { + "fullName": "", + "functionType": "ActionWithOperator", + "getterName": "IdleValue", + "group": "Button effect tween configuration", + "name": "SetIdleValue", + "sentence": "", + "events": [ + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [], + "actions": [ + { + "type": { + "value": "SetNumberVariable" + }, + "parameters": [ + "IdleValue", + "=", + "Value" + ] + } + ] + } + ], + "parameters": [ + { + "description": "Object", + "name": "Object", + "type": "object" + }, + { + "description": "Behavior", + "name": "Behavior", + "supplementaryInformation": "ButtonStates::ButtonObjectEffectTween", + "type": "behavior" + } + ], + "objectGroups": [] + }, + { + "description": "the focused effect parameter value of the object. The state is Focused when the button is hovered or held outside.", + "fullName": "Focused effect parameter value", + "functionType": "ExpressionAndCondition", + "group": "Button effect tween configuration", + "name": "FocusedValue", + "sentence": "the focused effect parameter value", + "events": [ + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [], + "actions": [ + { + "type": { + "value": "SetReturnNumber" + }, + "parameters": [ + "FocusedValue" + ] + } + ] + } + ], + "expressionType": { + "type": "expression" + }, + "parameters": [ + { + "description": "Object", + "name": "Object", + "type": "object" + }, + { + "description": "Behavior", + "name": "Behavior", + "supplementaryInformation": "ButtonStates::ButtonObjectEffectTween", + "type": "behavior" + } + ], + "objectGroups": [] + }, + { + "fullName": "", + "functionType": "ActionWithOperator", + "getterName": "FocusedValue", + "group": "Button effect tween configuration", + "name": "SetFocusedValue", + "sentence": "", + "events": [ + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [], + "actions": [ + { + "type": { + "value": "SetNumberVariable" + }, + "parameters": [ + "FocusedValue", + "=", + "Value" + ] + } + ] + } + ], + "parameters": [ + { + "description": "Object", + "name": "Object", + "type": "object" + }, + { + "description": "Behavior", + "name": "Behavior", + "supplementaryInformation": "ButtonStates::ButtonObjectEffectTween", + "type": "behavior" + } + ], + "objectGroups": [] + }, + { + "description": "the pressed effect parameter value of the object.", + "fullName": "Pressed effect parameter value", + "functionType": "ExpressionAndCondition", + "group": "Button effect tween configuration", + "name": "PressedValue", + "sentence": "the pressed effect parameter value", + "events": [ + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [], + "actions": [ + { + "type": { + "value": "SetReturnNumber" + }, + "parameters": [ + "PressedValue" + ] + } + ] + } + ], + "expressionType": { + "type": "expression" + }, + "parameters": [ + { + "description": "Object", + "name": "Object", + "type": "object" + }, + { + "description": "Behavior", + "name": "Behavior", + "supplementaryInformation": "ButtonStates::ButtonObjectEffectTween", + "type": "behavior" + } + ], + "objectGroups": [] + }, + { + "fullName": "", + "functionType": "ActionWithOperator", + "getterName": "PressedValue", + "group": "Button effect tween configuration", + "name": "SetPressedValue", + "sentence": "", + "events": [ + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [], + "actions": [ + { + "type": { + "value": "SetNumberVariable" + }, + "parameters": [ + "PressedValue", + "=", + "Value" + ] + } + ] + } + ], + "parameters": [ + { + "description": "Object", + "name": "Object", + "type": "object" + }, + { + "description": "Behavior", + "name": "Behavior", + "supplementaryInformation": "ButtonStates::ButtonObjectEffectTween", + "type": "behavior" + } + ], + "objectGroups": [] + }, + { + "description": "the fade-in easing of the object.", + "fullName": "Fade-in easing", + "functionType": "ExpressionAndCondition", + "group": "Button effect tween configuration", + "name": "FadeInEasing", + "sentence": "the fade-in easing", + "events": [ + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [], + "actions": [ + { + "type": { + "value": "SetReturnString" + }, + "parameters": [ + "FadeInEasing" + ] + } + ] + } + ], + "expressionType": { + "supplementaryInformation": "[\"linear\",\"easeInQuad\",\"easeOutQuad\",\"easeInOutQuad\",\"easeInCubic\",\"easeOutCubic\",\"easeInOutCubic\",\"easeInQuart\",\"easeOutQuart\",\"easeInOutQuart\",\"easeInQuint\",\"easeOutQuint\",\"easeInOutQuint\",\"easeInOutSine\",\"easeInExpo\",\"easeOutExpo\",\"easeInOutExpo\",\"easeInCirc\",\"easeOutCirc\",\"easeInOutCirc\",\"easeOutBounce\",\"easeInBack\",\"easeOutBack\",\"easeInOutBack\",\"elastic\",\"swingFromTo\",\"swingFrom\",\"swingTo\",\"bounce\",\"bouncePast\",\"easeFromTo\",\"easeFrom\",\"easeTo\"]", + "type": "stringWithSelector" + }, + "parameters": [ + { + "description": "Object", + "name": "Object", + "type": "object" + }, + { + "description": "Behavior", + "name": "Behavior", + "supplementaryInformation": "ButtonStates::ButtonObjectEffectTween", + "type": "behavior" + } + ], + "objectGroups": [] + }, + { + "fullName": "", + "functionType": "ActionWithOperator", + "getterName": "FadeInEasing", + "group": "Button effect tween configuration", + "name": "SetFadeInEasing", + "sentence": "", + "events": [ + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [], + "actions": [ + { + "type": { + "value": "SetStringVariable" + }, + "parameters": [ + "FadeInEasing", + "=", + "Value" + ] + } + ] + } + ], + "parameters": [ + { + "description": "Object", + "name": "Object", + "type": "object" + }, + { + "description": "Behavior", + "name": "Behavior", + "supplementaryInformation": "ButtonStates::ButtonObjectEffectTween", + "type": "behavior" + } + ], + "objectGroups": [] + }, + { + "description": "the fade-out easing of the object.", + "fullName": "Fade-out easing", + "functionType": "ExpressionAndCondition", + "group": "Button effect tween configuration", + "name": "FadeOutEasing", + "sentence": "the fade-out easing", + "events": [ + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [], + "actions": [ + { + "type": { + "value": "SetReturnString" + }, + "parameters": [ + "FadeOutEasing" + ] + } + ] + } + ], + "expressionType": { + "supplementaryInformation": "[\"linear\",\"easeInQuad\",\"easeOutQuad\",\"easeInOutQuad\",\"easeInCubic\",\"easeOutCubic\",\"easeInOutCubic\",\"easeInQuart\",\"easeOutQuart\",\"easeInOutQuart\",\"easeInQuint\",\"easeOutQuint\",\"easeInOutQuint\",\"easeInOutSine\",\"easeInExpo\",\"easeOutExpo\",\"easeInOutExpo\",\"easeInCirc\",\"easeOutCirc\",\"easeInOutCirc\",\"easeOutBounce\",\"easeInBack\",\"easeOutBack\",\"easeInOutBack\",\"elastic\",\"swingFromTo\",\"swingFrom\",\"swingTo\",\"bounce\",\"bouncePast\",\"easeFromTo\",\"easeFrom\",\"easeTo\"]", + "type": "stringWithSelector" + }, + "parameters": [ + { + "description": "Object", + "name": "Object", + "type": "object" + }, + { + "description": "Behavior", + "name": "Behavior", + "supplementaryInformation": "ButtonStates::ButtonObjectEffectTween", + "type": "behavior" + } + ], + "objectGroups": [] + }, + { + "fullName": "", + "functionType": "ActionWithOperator", + "getterName": "FadeOutEasing", + "group": "Button effect tween configuration", + "name": "SetFadeOutEasing", + "sentence": "", + "events": [ + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [], + "actions": [ + { + "type": { + "value": "SetStringVariable" + }, + "parameters": [ + "FadeOutEasing", + "=", + "Value" + ] + } + ] + } + ], + "parameters": [ + { + "description": "Object", + "name": "Object", + "type": "object" + }, + { + "description": "Behavior", + "name": "Behavior", + "supplementaryInformation": "ButtonStates::ButtonObjectEffectTween", + "type": "behavior" + } + ], + "objectGroups": [] + }, + { + "description": "the fade-in duration of the object.", + "fullName": "Fade-in duration", + "functionType": "ExpressionAndCondition", + "group": "Button effect tween configuration", + "name": "FadeInDuration", + "sentence": "the fade-in duration", + "events": [ + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [], + "actions": [ + { + "type": { + "value": "SetReturnNumber" + }, + "parameters": [ + "FadeInDuration" + ] + } + ] + } + ], + "expressionType": { + "type": "expression" + }, + "parameters": [ + { + "description": "Object", + "name": "Object", + "type": "object" + }, + { + "description": "Behavior", + "name": "Behavior", + "supplementaryInformation": "ButtonStates::ButtonObjectEffectTween", + "type": "behavior" + } + ], + "objectGroups": [] + }, + { + "fullName": "", + "functionType": "ActionWithOperator", + "getterName": "FadeInDuration", + "group": "Button effect tween configuration", + "name": "SetFadeInDuration", + "sentence": "", + "events": [ + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [], + "actions": [ + { + "type": { + "value": "SetNumberVariable" + }, + "parameters": [ + "FadeInDuration", + "=", + "Value" + ] + } + ] + } + ], + "parameters": [ + { + "description": "Object", + "name": "Object", + "type": "object" + }, + { + "description": "Behavior", + "name": "Behavior", + "supplementaryInformation": "ButtonStates::ButtonObjectEffectTween", + "type": "behavior" + } + ], + "objectGroups": [] + }, + { + "description": "the fade-out duration of the object.", + "fullName": "Fade-out duration", + "functionType": "ExpressionAndCondition", + "group": "Button effect tween configuration", + "name": "FadeOutDuration", + "sentence": "the fade-out duration", + "events": [ + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [], + "actions": [ + { + "type": { + "value": "SetReturnNumber" + }, + "parameters": [ + "FadeOutDuration" + ] + } + ] + } + ], + "expressionType": { + "type": "expression" + }, + "parameters": [ + { + "description": "Object", + "name": "Object", + "type": "object" + }, + { + "description": "Behavior", + "name": "Behavior", + "supplementaryInformation": "ButtonStates::ButtonObjectEffectTween", + "type": "behavior" + } + ], + "objectGroups": [] + }, + { + "fullName": "", + "functionType": "ActionWithOperator", + "getterName": "FadeOutDuration", + "group": "Button effect tween configuration", + "name": "SetFadeOutDuration", + "sentence": "", + "events": [ + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [], + "actions": [ + { + "type": { + "value": "SetNumberVariable" + }, + "parameters": [ + "FadeOutDuration", + "=", + "Value" + ] + } + ] + } + ], + "parameters": [ + { + "description": "Object", + "name": "Object", + "type": "object" + }, + { + "description": "Behavior", + "name": "Behavior", + "supplementaryInformation": "ButtonStates::ButtonObjectEffectTween", + "type": "behavior" + } + ], + "objectGroups": [] + } + ], + "eventsFunctionsFolderStructure": { + "folderName": "__ROOT", + "children": [ + { + "functionName": "onCreated" + }, + { + "functionName": "doStepPreEvents" + }, + { + "functionName": "TimeDelta" + }, + { + "functionName": "FadeIn" + }, + { + "functionName": "FadeOut" + }, + { + "functionName": "PlayTween" + }, + { + "folderName": "Button effect tween configuration", + "children": [ + { + "functionName": "EffectName" + }, + { + "functionName": "EffectProperty" + }, + { + "functionName": "SetEffectProperty" + }, + { + "functionName": "IdleValue" + }, + { + "functionName": "SetIdleValue" + }, + { + "functionName": "FocusedValue" + }, + { + "functionName": "SetFocusedValue" + }, + { + "functionName": "PressedValue" + }, + { + "functionName": "SetPressedValue" + }, + { + "functionName": "FadeInEasing" + }, + { + "functionName": "SetFadeInEasing" + }, + { + "functionName": "FadeOutEasing" + }, + { + "functionName": "SetFadeOutEasing" + }, + { + "functionName": "FadeInDuration" + }, + { + "functionName": "SetFadeInDuration" + }, + { + "functionName": "FadeOutDuration" + }, + { + "functionName": "SetFadeOutDuration" + } + ] + } + ] + }, + "propertyDescriptors": [ + { + "value": "", + "type": "Behavior", + "label": "Button states", + "extraInformation": [ + "ButtonStates::ButtonFSM" + ], + "choices": [], + "name": "ButtonFSM" + }, + { + "value": "", + "type": "Behavior", + "label": "Effect capability", + "extraInformation": [ + "EffectCapability::EffectBehavior" + ], + "choices": [], + "name": "Effect" + }, + { + "value": "Effect", + "type": "String", + "label": "Effect name", + "group": "Effect", + "name": "EffectName" + }, + { + "value": "", + "type": "String", + "label": "Effect parameter", + "description": "The effect parameter names can be found in the effects tab with the \"Show parameter names\" action of the drop down menu.", + "group": "Effect", + "name": "EffectProperty" + }, + { + "value": "0", + "type": "Number", + "label": "Idle effect parameter value", + "group": "Value", + "name": "IdleValue" + }, + { + "value": "0", + "type": "Number", + "label": "Focused effect parameter value", + "description": "The state is Focused when the button is hovered or held outside.", + "group": "Value", + "name": "FocusedValue" + }, + { + "value": "0", + "type": "Number", + "label": "Pressed effect parameter value", + "group": "Value", + "name": "PressedValue" + }, + { + "value": "easeInOutQuad", + "type": "Choice", + "label": "Fade-in easing", + "group": "Speed", + "choices": [ + { + "label": "linear", + "value": "linear" + }, + { + "label": "easeInQuad", + "value": "easeInQuad" + }, + { + "label": "easeOutQuad", + "value": "easeOutQuad" + }, + { + "label": "easeInOutQuad", + "value": "easeInOutQuad" + }, + { + "label": "easeInCubic", + "value": "easeInCubic" + }, + { + "label": "easeOutCubic", + "value": "easeOutCubic" + }, + { + "label": "easeInOutCubic", + "value": "easeInOutCubic" + }, + { + "label": "easeInQuart", + "value": "easeInQuart" + }, + { + "label": "easeOutQuart", + "value": "easeOutQuart" + }, + { + "label": "easeInOutQuart", + "value": "easeInOutQuart" + }, + { + "label": "easeInQuint", + "value": "easeInQuint" + }, + { + "label": "easeOutQuint", + "value": "easeOutQuint" + }, + { + "label": "easeInOutQuint", + "value": "easeInOutQuint" + }, + { + "label": "easeInOutSine", + "value": "easeInOutSine" + }, + { + "label": "easeInExpo", + "value": "easeInExpo" + }, + { + "label": "easeOutExpo", + "value": "easeOutExpo" + }, + { + "label": "easeInOutExpo", + "value": "easeInOutExpo" + }, + { + "label": "easeInCirc", + "value": "easeInCirc" + }, + { + "label": "easeOutCirc", + "value": "easeOutCirc" + }, + { + "label": "easeInOutCirc", + "value": "easeInOutCirc" + }, + { + "label": "easeOutBounce", + "value": "easeOutBounce" + }, + { + "label": "easeInBack", + "value": "easeInBack" + }, + { + "label": "easeOutBack", + "value": "easeOutBack" + }, + { + "label": "easeInOutBack", + "value": "easeInOutBack" + }, + { + "label": "elastic", + "value": "elastic" + }, + { + "label": "swingFromTo", + "value": "swingFromTo" + }, + { + "label": "swingFrom", + "value": "swingFrom" + }, + { + "label": "swingTo", + "value": "swingTo" + }, + { + "label": "bounce", + "value": "bounce" + }, + { + "label": "bouncePast", + "value": "bouncePast" + }, + { + "label": "easeFromTo", + "value": "easeFromTo" + }, + { + "label": "easeFrom", + "value": "easeFrom" + }, + { + "label": "easeTo", + "value": "easeTo" + } + ], + "name": "FadeInEasing" + }, + { + "value": "easeInOutQuad", + "type": "Choice", + "label": "Fade-out easing", + "group": "Speed", + "choices": [ + { + "label": "linear", + "value": "linear" + }, + { + "label": "easeInQuad", + "value": "easeInQuad" + }, + { + "label": "easeOutQuad", + "value": "easeOutQuad" + }, + { + "label": "easeInOutQuad", + "value": "easeInOutQuad" + }, + { + "label": "easeInCubic", + "value": "easeInCubic" + }, + { + "label": "easeOutCubic", + "value": "easeOutCubic" + }, + { + "label": "easeInOutCubic", + "value": "easeInOutCubic" + }, + { + "label": "easeInQuart", + "value": "easeInQuart" + }, + { + "label": "easeOutQuart", + "value": "easeOutQuart" + }, + { + "label": "easeInOutQuart", + "value": "easeInOutQuart" + }, + { + "label": "easeInQuint", + "value": "easeInQuint" + }, + { + "label": "easeOutQuint", + "value": "easeOutQuint" + }, + { + "label": "easeInOutQuint", + "value": "easeInOutQuint" + }, + { + "label": "easeInOutSine", + "value": "easeInOutSine" + }, + { + "label": "easeInExpo", + "value": "easeInExpo" + }, + { + "label": "easeOutExpo", + "value": "easeOutExpo" + }, + { + "label": "easeInOutExpo", + "value": "easeInOutExpo" + }, + { + "label": "easeInCirc", + "value": "easeInCirc" + }, + { + "label": "easeOutCirc", + "value": "easeOutCirc" + }, + { + "label": "easeInOutCirc", + "value": "easeInOutCirc" + }, + { + "label": "easeOutBounce", + "value": "easeOutBounce" + }, + { + "label": "easeInBack", + "value": "easeInBack" + }, + { + "label": "easeOutBack", + "value": "easeOutBack" + }, + { + "label": "easeInOutBack", + "value": "easeInOutBack" + }, + { + "label": "elastic", + "value": "elastic" + }, + { + "label": "swingFromTo", + "value": "swingFromTo" + }, + { + "label": "swingFrom", + "value": "swingFrom" + }, + { + "label": "swingTo", + "value": "swingTo" + }, + { + "label": "bounce", + "value": "bounce" + }, + { + "label": "bouncePast", + "value": "bouncePast" + }, + { + "label": "easeFromTo", + "value": "easeFromTo" + }, + { + "label": "easeFrom", + "value": "easeFrom" + }, + { + "label": "easeTo", + "value": "easeTo" + } + ], + "name": "FadeOutEasing" + }, + { + "value": "0.125", + "type": "Number", + "unit": "Second", + "label": "Fade-in duration", + "group": "Speed", + "name": "FadeInDuration" + }, + { + "value": "0.5", + "type": "Number", + "unit": "Second", + "label": "Fade-out duration", + "group": "Speed", + "name": "FadeOutDuration" + }, + { + "value": "", + "type": "Boolean", + "label": "Disable the effect in idle state", + "advanced": true, + "name": "ShouldDisableEffectInIdle" + }, + { + "value": "Idle", + "type": "Choice", + "label": "", + "choices": [ + { + "label": "Idle", + "value": "Idle" + }, + { + "label": "Focused", + "value": "Focused" + }, + { + "label": "Pressed", + "value": "Pressed" + } + ], + "hidden": true, + "name": "PreviousState" + }, + { + "value": "0", + "type": "Number", + "label": "", + "hidden": true, + "name": "TweenInitialValue" + }, + { + "value": "0", + "type": "Number", + "label": "", + "hidden": true, + "name": "TweenTargetedValue" + }, + { + "value": "0", + "type": "Number", + "label": "", + "hidden": true, + "name": "TweenTime" + }, + { + "value": "NoTween", + "type": "Choice", + "label": "", + "choices": [ + { + "label": "NoTween", + "value": "NoTween" + }, + { + "label": "FadeIn", + "value": "FadeIn" + }, + { + "label": "FadeOut", + "value": "FadeOut" + } + ], + "hidden": true, + "name": "TweenState" + }, + { + "value": "0", + "type": "Number", + "label": "", + "hidden": true, + "name": "EffectValue" + } + ], + "propertiesFolderStructure": { + "folderName": "__ROOT", + "children": [ + { + "propertyName": "ButtonFSM" + }, + { + "propertyName": "Effect" + }, + { + "folderName": "Effect", + "children": [ + { + "propertyName": "EffectName" + }, + { + "propertyName": "EffectProperty" + } + ] + }, + { + "folderName": "Value", + "children": [ + { + "propertyName": "IdleValue" + }, + { + "propertyName": "FocusedValue" + }, + { + "propertyName": "PressedValue" + } + ] + }, + { + "folderName": "Speed", + "children": [ + { + "propertyName": "FadeInEasing" + }, + { + "propertyName": "FadeOutEasing" + }, + { + "propertyName": "FadeInDuration" + }, + { + "propertyName": "FadeOutDuration" + } + ] + }, + { + "propertyName": "ShouldDisableEffectInIdle" + }, + { + "propertyName": "PreviousState" + }, + { + "propertyName": "TweenInitialValue" + }, + { + "propertyName": "TweenTargetedValue" + }, + { + "propertyName": "TweenTime" + }, + { + "propertyName": "TweenState" + }, + { + "propertyName": "EffectValue" + } + ] + } + }, + { + "description": "Smoothly resize buttons according to their state.", + "fullName": "Button scale tween", + "helpPath": "", + "iconUrl": "", + "name": "ButtonScaleTween", + "objectType": "", + "previewIconUrl": "", + "eventsFunctions": [ + { + "fullName": "", + "functionType": "Action", + "name": "onCreated", + "sentence": "", + "events": [ + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [], + "actions": [ + { + "type": { + "value": "ScalableCapability::ScalableBehavior::SetValue" + }, + "parameters": [ + "Object", + "Scale", + "=", + "IdleScale" + ] + } + ] + } + ], + "parameters": [ + { + "description": "Object", + "name": "Object", + "type": "object" + }, + { + "description": "Behavior", + "name": "Behavior", + "supplementaryInformation": "ButtonStates::ButtonScaleTween", + "type": "behavior" + } + ], + "objectGroups": [] + }, + { + "fullName": "", + "functionType": "Action", + "name": "doStepPreEvents", + "sentence": "", + "events": [ + { + "colorB": 228, + "colorG": 176, + "colorR": 74, + "creationTime": 0, + "name": "Change based on state", + "source": "", + "type": "BuiltinCommonInstructions::Group", + "events": [ + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [ + { + "type": { + "value": "ButtonStates::ButtonFSM::IsIdle" + }, + "parameters": [ + "Object", + "ButtonFSM", + "" + ] + }, + { + "type": { + "value": "StringVariable" + }, + "parameters": [ + "PreviousState", + "!=", + "\"Idle\"" + ] + } + ], + "actions": [ + { + "type": { + "value": "SetStringVariable" + }, + "parameters": [ + "PreviousState", + "=", + "\"Idle\"" + ] + }, + { + "type": { + "value": "ButtonStates::ButtonScaleTween::FadeOut" + }, + "parameters": [ + "Object", + "Behavior", + "IdleScale", + "" + ] + } + ] + }, + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [ + { + "type": { + "value": "ButtonStates::ButtonFSM::IsFocused" + }, + "parameters": [ + "Object", + "ButtonFSM", + "" + ] + } + ], + "actions": [], + "events": [ + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [ + { + "type": { + "value": "StringVariable" + }, + "parameters": [ + "PreviousState", + "=", + "\"Idle\"" + ] + } + ], + "actions": [ + { + "type": { + "value": "SetStringVariable" + }, + "parameters": [ + "PreviousState", + "=", + "\"Focused\"" + ] + }, + { + "type": { + "value": "ButtonStates::ButtonScaleTween::FadeIn" + }, + "parameters": [ + "Object", + "Behavior", + "FocusedScale", + "" + ] + } + ] + }, + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [ + { + "type": { + "value": "StringVariable" + }, + "parameters": [ + "PreviousState", + "=", + "\"Pressed\"" + ] + } + ], + "actions": [ + { + "type": { + "value": "SetStringVariable" + }, + "parameters": [ + "PreviousState", + "=", + "\"Focused\"" + ] + }, + { + "type": { + "value": "ButtonStates::ButtonScaleTween::FadeOut" + }, + "parameters": [ + "Object", + "Behavior", + "FocusedScale", + "" + ] + } + ] + } + ] + }, + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [ + { + "type": { + "value": "ButtonStates::ButtonFSM::IsPressed" + }, + "parameters": [ + "Object", + "ButtonFSM", + "" + ] + }, + { + "type": { + "value": "StringVariable" + }, + "parameters": [ + "PreviousState", + "!=", + "\"Pressed\"" + ] + } + ], + "actions": [ + { + "type": { + "value": "SetStringVariable" + }, + "parameters": [ + "PreviousState", + "=", + "\"Pressed\"" + ] + }, + { + "type": { + "value": "ButtonStates::ButtonScaleTween::FadeIn" + }, + "parameters": [ + "Object", + "Behavior", + "PressedScale", + "" + ] + } + ] + } + ], + "parameters": [] + } + ], + "parameters": [ + { + "description": "Object", + "name": "Object", + "type": "object" + }, + { + "description": "Behavior", + "name": "Behavior", + "supplementaryInformation": "ButtonStates::ButtonScaleTween", + "type": "behavior" + } + ], + "objectGroups": [] + }, + { + "fullName": "Fade in", + "functionType": "Action", + "name": "FadeIn", + "private": true, + "sentence": "_PARAM0_ fade in to _PARAM2_", + "events": [ + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [], + "actions": [ + { + "type": { + "value": "Tween::AddObjectScaleTween" + }, + "parameters": [ + "Object", + "Tween", + "\"__ButtonScaleTween.Fade\"", + "Value", + "Value", + "FadeInEasing", + "1000 * FadeInDuration", + "no", + "yes" + ] + } + ] + } + ], + "parameters": [ + { + "description": "Object", + "name": "Object", + "type": "object" + }, + { + "description": "Behavior", + "name": "Behavior", + "supplementaryInformation": "ButtonStates::ButtonScaleTween", + "type": "behavior" + }, + { + "description": "Value", + "name": "Value", + "type": "expression" + } + ], + "objectGroups": [] + }, + { + "fullName": "Fade out", + "functionType": "Action", + "name": "FadeOut", + "private": true, + "sentence": "_PARAM0_ fade out to _PARAM2_", + "events": [ + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [], + "actions": [ + { + "type": { + "value": "Tween::AddObjectScaleTween" + }, + "parameters": [ + "Object", + "Tween", + "\"__ButtonScaleTween.Fade\"", + "Value", + "Value", + "FadeOutEasing", + "1000 * FadeOutDuration", + "no", + "yes" + ] + } + ] + } + ], + "parameters": [ + { + "description": "Object", + "name": "Object", + "type": "object" + }, + { + "description": "Behavior", + "name": "Behavior", + "supplementaryInformation": "ButtonStates::ButtonScaleTween", + "type": "behavior" + }, + { + "description": "Value", + "name": "Value", + "type": "expression" + } + ], + "objectGroups": [] + }, + { + "description": "the idle state size scale of the object.", + "fullName": "Idle state size scale", + "functionType": "ExpressionAndCondition", + "group": "Button scale tween configuration", + "name": "IdleScale", + "sentence": "the idle state size scale", + "events": [ + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [], + "actions": [ + { + "type": { + "value": "SetReturnNumber" + }, + "parameters": [ + "IdleScale" + ] + } + ] + } + ], + "expressionType": { + "type": "expression" + }, + "parameters": [ + { + "description": "Object", + "name": "Object", + "type": "object" + }, + { + "description": "Behavior", + "name": "Behavior", + "supplementaryInformation": "ButtonStates::ButtonScaleTween", + "type": "behavior" + } + ], + "objectGroups": [] + }, + { + "fullName": "", + "functionType": "ActionWithOperator", + "getterName": "IdleScale", + "group": "Button scale tween configuration", + "name": "SetIdleScale", + "sentence": "", + "events": [ + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [], + "actions": [ + { + "type": { + "value": "SetNumberVariable" + }, + "parameters": [ + "IdleScale", + "=", + "Value" + ] + } + ] + } + ], + "parameters": [ + { + "description": "Object", + "name": "Object", + "type": "object" + }, + { + "description": "Behavior", + "name": "Behavior", + "supplementaryInformation": "ButtonStates::ButtonScaleTween", + "type": "behavior" + } + ], + "objectGroups": [] + }, + { + "description": "the focused state size scale of the object. The state is Focused when the button is hovered or held outside.", + "fullName": "Focused state size scale", + "functionType": "ExpressionAndCondition", + "group": "Button scale tween configuration", + "name": "FocusedScale", + "sentence": "the focused state size scale", + "events": [ + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [], + "actions": [ + { + "type": { + "value": "SetReturnNumber" + }, + "parameters": [ + "FocusedScale" + ] + } + ] + } + ], + "expressionType": { + "type": "expression" + }, + "parameters": [ + { + "description": "Object", + "name": "Object", + "type": "object" + }, + { + "description": "Behavior", + "name": "Behavior", + "supplementaryInformation": "ButtonStates::ButtonScaleTween", + "type": "behavior" + } + ], + "objectGroups": [] + }, + { + "fullName": "", + "functionType": "ActionWithOperator", + "getterName": "FocusedScale", + "group": "Button scale tween configuration", + "name": "SetFocusedScale", + "sentence": "", + "events": [ + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [], + "actions": [ + { + "type": { + "value": "SetNumberVariable" + }, + "parameters": [ + "FocusedScale", + "=", + "Value" + ] + } + ] + } + ], + "parameters": [ + { + "description": "Object", + "name": "Object", + "type": "object" + }, + { + "description": "Behavior", + "name": "Behavior", + "supplementaryInformation": "ButtonStates::ButtonScaleTween", + "type": "behavior" + } + ], + "objectGroups": [] + }, + { + "description": "the pressed state size scale of the object.", + "fullName": "Pressed state size scale", + "functionType": "ExpressionAndCondition", + "group": "Button scale tween configuration", + "name": "PressedScale", + "sentence": "the pressed state size scale", + "events": [ + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [], + "actions": [ + { + "type": { + "value": "SetReturnNumber" + }, + "parameters": [ + "PressedScale" + ] + } + ] + } + ], + "expressionType": { + "type": "expression" + }, + "parameters": [ + { + "description": "Object", + "name": "Object", + "type": "object" + }, + { + "description": "Behavior", + "name": "Behavior", + "supplementaryInformation": "ButtonStates::ButtonScaleTween", + "type": "behavior" + } + ], + "objectGroups": [] + }, + { + "fullName": "", + "functionType": "ActionWithOperator", + "getterName": "PressedScale", + "group": "Button scale tween configuration", + "name": "SetPressedScale", + "sentence": "", + "events": [ + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [], + "actions": [ + { + "type": { + "value": "SetNumberVariable" + }, + "parameters": [ + "PressedScale", + "=", + "Value" + ] + } + ] + } + ], + "parameters": [ + { + "description": "Object", + "name": "Object", + "type": "object" + }, + { + "description": "Behavior", + "name": "Behavior", + "supplementaryInformation": "ButtonStates::ButtonScaleTween", + "type": "behavior" + } + ], + "objectGroups": [] + }, + { + "description": "the fade-in duration of the object.", + "fullName": "Fade-in duration", + "functionType": "ExpressionAndCondition", + "group": "Button scale tween configuration", + "name": "FadeInDuration", + "sentence": "the fade-in duration", + "events": [ + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [], + "actions": [ + { + "type": { + "value": "SetReturnNumber" + }, + "parameters": [ + "FadeInDuration" + ] + } + ] + } + ], + "expressionType": { + "type": "expression" + }, + "parameters": [ + { + "description": "Object", + "name": "Object", + "type": "object" + }, + { + "description": "Behavior", + "name": "Behavior", + "supplementaryInformation": "ButtonStates::ButtonScaleTween", + "type": "behavior" + } + ], + "objectGroups": [] + }, + { + "fullName": "", + "functionType": "ActionWithOperator", + "getterName": "FadeInDuration", + "group": "Button scale tween configuration", + "name": "SetFadeInDuration", + "sentence": "", + "events": [ + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [], + "actions": [ + { + "type": { + "value": "SetNumberVariable" + }, + "parameters": [ + "FadeInDuration", + "=", + "Value" + ] + } + ] + } + ], + "parameters": [ + { + "description": "Object", + "name": "Object", + "type": "object" + }, + { + "description": "Behavior", + "name": "Behavior", + "supplementaryInformation": "ButtonStates::ButtonScaleTween", + "type": "behavior" + } + ], + "objectGroups": [] + }, + { + "description": "the fade-out duration of the object.", + "fullName": "Fade-out duration", + "functionType": "ExpressionAndCondition", + "group": "Button scale tween configuration", + "name": "FadeOutDuration", + "sentence": "the fade-out duration", + "events": [ + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [], + "actions": [ + { + "type": { + "value": "SetReturnNumber" + }, + "parameters": [ + "FadeOutDuration" + ] + } + ] + } + ], + "expressionType": { + "type": "expression" + }, + "parameters": [ + { + "description": "Object", + "name": "Object", + "type": "object" + }, + { + "description": "Behavior", + "name": "Behavior", + "supplementaryInformation": "ButtonStates::ButtonScaleTween", + "type": "behavior" + } + ], + "objectGroups": [] + }, + { + "fullName": "", + "functionType": "ActionWithOperator", + "getterName": "FadeOutDuration", + "group": "Button scale tween configuration", + "name": "SetFadeOutDuration", + "sentence": "", + "events": [ + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [], + "actions": [ + { + "type": { + "value": "SetNumberVariable" + }, + "parameters": [ + "FadeOutDuration", + "=", + "Value" + ] + } + ] + } + ], + "parameters": [ + { + "description": "Object", + "name": "Object", + "type": "object" + }, + { + "description": "Behavior", + "name": "Behavior", + "supplementaryInformation": "ButtonStates::ButtonScaleTween", + "type": "behavior" + } + ], + "objectGroups": [] + }, + { + "description": "the fade-in easing of the object.", + "fullName": "Fade-in easing", + "functionType": "ExpressionAndCondition", + "group": "Button scale tween configuration", + "name": "FadeInEasing", + "sentence": "the fade-in easing", + "events": [ + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [], + "actions": [ + { + "type": { + "value": "SetReturnString" + }, + "parameters": [ + "FadeInEasing" + ] + } + ] + } + ], + "expressionType": { + "supplementaryInformation": "[\"linear\",\"easeInQuad\",\"easeOutQuad\",\"easeInOutQuad\",\"easeInCubic\",\"easeOutCubic\",\"easeInOutCubic\",\"easeInQuart\",\"easeOutQuart\",\"easeInOutQuart\",\"easeInQuint\",\"easeOutQuint\",\"easeInOutQuint\",\"easeInOutSine\",\"easeInExpo\",\"easeOutExpo\",\"easeInOutExpo\",\"easeInCirc\",\"easeOutCirc\",\"easeInOutCirc\",\"easeOutBounce\",\"easeInBack\",\"easeOutBack\",\"easeInOutBack\",\"elastic\",\"swingFromTo\",\"swingFrom\",\"swingTo\",\"bounce\",\"bouncePast\",\"easeFromTo\",\"easeFrom\",\"easeTo\"]", + "type": "stringWithSelector" + }, + "parameters": [ + { + "description": "Object", + "name": "Object", + "type": "object" + }, + { + "description": "Behavior", + "name": "Behavior", + "supplementaryInformation": "ButtonStates::ButtonScaleTween", + "type": "behavior" + } + ], + "objectGroups": [] + }, + { + "fullName": "", + "functionType": "ActionWithOperator", + "getterName": "FadeInEasing", + "group": "Button scale tween configuration", + "name": "SetFadeInEasing", + "sentence": "", + "events": [ + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [], + "actions": [ + { + "type": { + "value": "SetStringVariable" + }, + "parameters": [ + "FadeInEasing", + "=", + "Value" + ] + } + ] + } + ], + "parameters": [ + { + "description": "Object", + "name": "Object", + "type": "object" + }, + { + "description": "Behavior", + "name": "Behavior", + "supplementaryInformation": "ButtonStates::ButtonScaleTween", + "type": "behavior" + } + ], + "objectGroups": [] + }, + { + "description": "the fade-out easing of the object.", + "fullName": "Fade-out easing", + "functionType": "ExpressionAndCondition", + "group": "Button scale tween configuration", + "name": "FadeOutEasing", + "sentence": "the fade-out easing", + "events": [ + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [], + "actions": [ + { + "type": { + "value": "SetReturnString" + }, + "parameters": [ + "FadeOutEasing" + ] + } + ] + } + ], + "expressionType": { + "supplementaryInformation": "[\"linear\",\"easeInQuad\",\"easeOutQuad\",\"easeInOutQuad\",\"easeInCubic\",\"easeOutCubic\",\"easeInOutCubic\",\"easeInQuart\",\"easeOutQuart\",\"easeInOutQuart\",\"easeInQuint\",\"easeOutQuint\",\"easeInOutQuint\",\"easeInOutSine\",\"easeInExpo\",\"easeOutExpo\",\"easeInOutExpo\",\"easeInCirc\",\"easeOutCirc\",\"easeInOutCirc\",\"easeOutBounce\",\"easeInBack\",\"easeOutBack\",\"easeInOutBack\",\"elastic\",\"swingFromTo\",\"swingFrom\",\"swingTo\",\"bounce\",\"bouncePast\",\"easeFromTo\",\"easeFrom\",\"easeTo\"]", + "type": "stringWithSelector" + }, + "parameters": [ + { + "description": "Object", + "name": "Object", + "type": "object" + }, + { + "description": "Behavior", + "name": "Behavior", + "supplementaryInformation": "ButtonStates::ButtonScaleTween", + "type": "behavior" + } + ], + "objectGroups": [] + }, + { + "fullName": "", + "functionType": "ActionWithOperator", + "getterName": "FadeOutEasing", + "group": "Button scale tween configuration", + "name": "SetFadeOutEasing", + "sentence": "", + "events": [ + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [], + "actions": [ + { + "type": { + "value": "SetStringVariable" + }, + "parameters": [ + "FadeOutEasing", + "=", + "Value" + ] + } + ] + } + ], + "parameters": [ + { + "description": "Object", + "name": "Object", + "type": "object" + }, + { + "description": "Behavior", + "name": "Behavior", + "supplementaryInformation": "ButtonStates::ButtonScaleTween", + "type": "behavior" + } + ], + "objectGroups": [] + } + ], + "eventsFunctionsFolderStructure": { + "folderName": "__ROOT", + "children": [ + { + "functionName": "onCreated" + }, + { + "functionName": "doStepPreEvents" + }, + { + "functionName": "FadeIn" + }, + { + "functionName": "FadeOut" + }, + { + "folderName": "Button scale tween configuration", + "children": [ + { + "functionName": "IdleScale" + }, + { + "functionName": "SetIdleScale" + }, + { + "functionName": "FocusedScale" + }, + { + "functionName": "SetFocusedScale" + }, + { + "functionName": "PressedScale" + }, + { + "functionName": "SetPressedScale" + }, + { + "functionName": "FadeInDuration" + }, + { + "functionName": "SetFadeInDuration" + }, + { + "functionName": "FadeOutDuration" + }, + { + "functionName": "SetFadeOutDuration" + }, + { + "functionName": "FadeInEasing" + }, + { + "functionName": "SetFadeInEasing" + }, + { + "functionName": "FadeOutEasing" + }, + { + "functionName": "SetFadeOutEasing" + } + ] + } + ] + }, + "propertyDescriptors": [ + { + "value": "", + "type": "Behavior", + "label": "Scalable capability", + "extraInformation": [ + "ScalableCapability::ScalableBehavior" + ], + "choices": [], + "name": "Scale" + }, + { + "value": "", + "type": "Behavior", + "label": "Button states behavior (required)", + "extraInformation": [ + "ButtonStates::ButtonFSM" + ], + "choices": [], + "name": "ButtonFSM" + }, + { + "value": "", + "type": "Behavior", + "label": "Tween behavior (required)", + "extraInformation": [ + "Tween::TweenBehavior" + ], + "choices": [], + "name": "Tween" + }, + { + "value": "1", + "type": "Number", + "unit": "Dimensionless", + "label": "Idle state size scale", + "group": "Size", + "name": "IdleScale" + }, + { + "value": "1", + "type": "Number", + "unit": "Dimensionless", + "label": "Focused state size scale", + "description": "The state is Focused when the button is hovered or held outside.", + "group": "Size", + "name": "FocusedScale" + }, + { + "value": "0.1", + "type": "Number", + "unit": "Second", + "label": "Fade-in duration", + "group": "Speed", + "name": "FadeInDuration" + }, + { + "value": "0.2", + "type": "Number", + "unit": "Second", + "label": "Fade-out duration", + "group": "Speed", + "name": "FadeOutDuration" + }, + { + "value": "0.95", + "type": "Number", + "unit": "Dimensionless", + "label": "Pressed state size scale", + "group": "Size", + "name": "PressedScale" + }, + { + "value": "easeInOutQuad", + "type": "Choice", + "label": "Fade-in easing", + "group": "Speed", + "choices": [ + { + "label": "linear", + "value": "linear" + }, + { + "label": "easeInQuad", + "value": "easeInQuad" + }, + { + "label": "easeOutQuad", + "value": "easeOutQuad" + }, + { + "label": "easeInOutQuad", + "value": "easeInOutQuad" + }, + { + "label": "easeInCubic", + "value": "easeInCubic" + }, + { + "label": "easeOutCubic", + "value": "easeOutCubic" + }, + { + "label": "easeInOutCubic", + "value": "easeInOutCubic" + }, + { + "label": "easeInQuart", + "value": "easeInQuart" + }, + { + "label": "easeOutQuart", + "value": "easeOutQuart" + }, + { + "label": "easeInOutQuart", + "value": "easeInOutQuart" + }, + { + "label": "easeInQuint", + "value": "easeInQuint" + }, + { + "label": "easeOutQuint", + "value": "easeOutQuint" + }, + { + "label": "easeInOutQuint", + "value": "easeInOutQuint" + }, + { + "label": "easeInOutSine", + "value": "easeInOutSine" + }, + { + "label": "easeInExpo", + "value": "easeInExpo" + }, + { + "label": "easeOutExpo", + "value": "easeOutExpo" + }, + { + "label": "easeInOutExpo", + "value": "easeInOutExpo" + }, + { + "label": "easeInCirc", + "value": "easeInCirc" + }, + { + "label": "easeOutCirc", + "value": "easeOutCirc" + }, + { + "label": "easeInOutCirc", + "value": "easeInOutCirc" + }, + { + "label": "easeOutBounce", + "value": "easeOutBounce" + }, + { + "label": "easeInBack", + "value": "easeInBack" + }, + { + "label": "easeOutBack", + "value": "easeOutBack" + }, + { + "label": "easeInOutBack", + "value": "easeInOutBack" + }, + { + "label": "elastic", + "value": "elastic" + }, + { + "label": "swingFromTo", + "value": "swingFromTo" + }, + { + "label": "swingFrom", + "value": "swingFrom" + }, + { + "label": "swingTo", + "value": "swingTo" + }, + { + "label": "bounce", + "value": "bounce" + }, + { + "label": "bouncePast", + "value": "bouncePast" + }, + { + "label": "easeFromTo", + "value": "easeFromTo" + }, + { + "label": "easeFrom", + "value": "easeFrom" + }, + { + "label": "easeTo", + "value": "easeTo" + } + ], + "name": "FadeInEasing" + }, + { + "value": "easeInOutQuad", + "type": "Choice", + "label": "Fade-out easing", + "group": "Speed", + "choices": [ + { + "label": "linear", + "value": "linear" + }, + { + "label": "easeInQuad", + "value": "easeInQuad" + }, + { + "label": "easeOutQuad", + "value": "easeOutQuad" + }, + { + "label": "easeInOutQuad", + "value": "easeInOutQuad" + }, + { + "label": "easeInCubic", + "value": "easeInCubic" + }, + { + "label": "easeOutCubic", + "value": "easeOutCubic" + }, + { + "label": "easeInOutCubic", + "value": "easeInOutCubic" + }, + { + "label": "easeInQuart", + "value": "easeInQuart" + }, + { + "label": "easeOutQuart", + "value": "easeOutQuart" + }, + { + "label": "easeInOutQuart", + "value": "easeInOutQuart" + }, + { + "label": "easeInQuint", + "value": "easeInQuint" + }, + { + "label": "easeOutQuint", + "value": "easeOutQuint" + }, + { + "label": "easeInOutQuint", + "value": "easeInOutQuint" + }, + { + "label": "easeInOutSine", + "value": "easeInOutSine" + }, + { + "label": "easeInExpo", + "value": "easeInExpo" + }, + { + "label": "easeOutExpo", + "value": "easeOutExpo" + }, + { + "label": "easeInOutExpo", + "value": "easeInOutExpo" + }, + { + "label": "easeInCirc", + "value": "easeInCirc" + }, + { + "label": "easeOutCirc", + "value": "easeOutCirc" + }, + { + "label": "easeInOutCirc", + "value": "easeInOutCirc" + }, + { + "label": "easeOutBounce", + "value": "easeOutBounce" + }, + { + "label": "easeInBack", + "value": "easeInBack" + }, + { + "label": "easeOutBack", + "value": "easeOutBack" + }, + { + "label": "easeInOutBack", + "value": "easeInOutBack" + }, + { + "label": "elastic", + "value": "elastic" + }, + { + "label": "swingFromTo", + "value": "swingFromTo" + }, + { + "label": "swingFrom", + "value": "swingFrom" + }, + { + "label": "swingTo", + "value": "swingTo" + }, + { + "label": "bounce", + "value": "bounce" + }, + { + "label": "bouncePast", + "value": "bouncePast" + }, + { + "label": "easeFromTo", + "value": "easeFromTo" + }, + { + "label": "easeFrom", + "value": "easeFrom" + }, + { + "label": "easeTo", + "value": "easeTo" + } + ], + "name": "FadeOutEasing" + }, + { + "value": "Idle", + "type": "Choice", + "label": "", + "choices": [ + { + "label": "Idle", + "value": "Idle" + }, + { + "label": "Focused", + "value": "Focused" + }, + { + "label": "Pressed", + "value": "Pressed" + } + ], + "hidden": true, + "name": "PreviousState" + } + ], + "propertiesFolderStructure": { + "folderName": "__ROOT", + "children": [ + { + "propertyName": "Scale" + }, + { + "propertyName": "ButtonFSM" + }, + { + "propertyName": "Tween" + }, + { + "folderName": "Size", + "children": [ + { + "propertyName": "IdleScale" + }, + { + "propertyName": "FocusedScale" + }, + { + "propertyName": "PressedScale" + } + ] + }, + { + "folderName": "Speed", + "children": [ + { + "propertyName": "FadeInDuration" + }, + { + "propertyName": "FadeOutDuration" + }, + { + "propertyName": "FadeInEasing" + }, + { + "propertyName": "FadeOutEasing" + } + ] + }, + { + "propertyName": "PreviousState" + } + ] + } + }, + { + "description": "Smoothly change the color tint of buttons according to their state.", + "fullName": "Button color tint tween", + "helpPath": "", + "iconUrl": "", + "name": "ButtonColorTintTween", + "objectType": "Sprite", + "previewIconUrl": "", + "eventsFunctions": [ + { + "fullName": "", + "functionType": "Action", + "name": "onCreated", + "sentence": "", + "events": [ + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [], + "actions": [ + { + "type": { + "value": "ChangeColor" + }, + "parameters": [ + "Object", + "IdleColorTint" + ] + } + ] + } + ], + "parameters": [ + { + "description": "Object", + "name": "Object", + "supplementaryInformation": "Sprite", + "type": "object" + }, + { + "description": "Behavior", + "name": "Behavior", + "supplementaryInformation": "ButtonStates::ButtonColorTintTween", + "type": "behavior" + } + ], + "objectGroups": [] + }, + { + "fullName": "", + "functionType": "Action", + "name": "doStepPreEvents", + "sentence": "", + "events": [ + { + "colorB": 228, + "colorG": 176, + "colorR": 74, + "creationTime": 0, + "name": "States", + "source": "", + "type": "BuiltinCommonInstructions::Group", + "events": [ + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [ + { + "type": { + "value": "ButtonStates::ButtonFSM::IsIdle" + }, + "parameters": [ + "Object", + "ButtonFSM", + "" + ] + }, + { + "type": { + "value": "StringVariable" + }, + "parameters": [ + "PreviousState", + "!=", + "\"Idle\"" + ] + } + ], + "actions": [ + { + "type": { + "value": "SetStringVariable" + }, + "parameters": [ + "PreviousState", + "=", + "\"Idle\"" + ] + }, + { + "type": { + "value": "ButtonStates::ButtonColorTintTween::FadeOut" + }, + "parameters": [ + "Object", + "Behavior", + "IdleColorTint", + "" + ] + } + ] + }, + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [ + { + "type": { + "value": "ButtonStates::ButtonFSM::IsFocused" + }, + "parameters": [ + "Object", + "ButtonFSM", + "" + ] + } + ], + "actions": [], + "events": [ + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [ + { + "type": { + "value": "StringVariable" + }, + "parameters": [ + "PreviousState", + "=", + "\"Idle\"" + ] + } + ], + "actions": [ + { + "type": { + "value": "SetStringVariable" + }, + "parameters": [ + "PreviousState", + "=", + "\"Focused\"" + ] + }, + { + "type": { + "value": "ButtonStates::ButtonColorTintTween::FadeIn" + }, + "parameters": [ + "Object", + "Behavior", + "FocusedColorTint", + "" + ] + } + ] + }, + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [ + { + "type": { + "value": "StringVariable" + }, + "parameters": [ + "PreviousState", + "=", + "\"Pressed\"" + ] + } + ], + "actions": [ + { + "type": { + "value": "SetStringVariable" + }, + "parameters": [ + "PreviousState", + "=", + "\"Focused\"" + ] + }, + { + "type": { + "value": "ButtonStates::ButtonColorTintTween::FadeOut" + }, + "parameters": [ + "Object", + "Behavior", + "FocusedColorTint", + "" + ] + } + ] + } + ] + }, + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [ + { + "type": { + "value": "ButtonStates::ButtonFSM::IsPressed" + }, + "parameters": [ + "Object", + "ButtonFSM", + "" + ] + }, + { + "type": { + "value": "StringVariable" + }, + "parameters": [ + "PreviousState", + "!=", + "\"Pressed\"" + ] + } + ], + "actions": [ + { + "type": { + "value": "SetStringVariable" + }, + "parameters": [ + "PreviousState", + "=", + "\"Pressed\"" + ] + }, + { + "type": { + "value": "ButtonStates::ButtonColorTintTween::FadeIn" + }, + "parameters": [ + "Object", + "Behavior", + "PressedColorTint", + "" + ] + } + ] + } + ], + "parameters": [] + } + ], + "parameters": [ + { + "description": "Object", + "name": "Object", + "supplementaryInformation": "Sprite", + "type": "object" + }, + { + "description": "Behavior", + "name": "Behavior", + "supplementaryInformation": "ButtonStates::ButtonColorTintTween", + "type": "behavior" + } + ], + "objectGroups": [] + }, + { + "fullName": "Fade in", + "functionType": "Action", + "name": "FadeIn", + "private": true, + "sentence": "_PARAM0_ fade in to _PARAM2_", + "events": [ + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [], + "actions": [ + { + "type": { + "value": "Tween::AddObjectColorTween" + }, + "parameters": [ + "Object", + "Tween", + "\"__ButtonColorTintTween.Fade\"", + "Value", + "FadeInEasing", + "1000 * FadeInDuration", + "no", + "yes" + ] + } + ] + } + ], + "parameters": [ + { + "description": "Object", + "name": "Object", + "supplementaryInformation": "Sprite", + "type": "object" + }, + { + "description": "Behavior", + "name": "Behavior", + "supplementaryInformation": "ButtonStates::ButtonColorTintTween", + "type": "behavior" + }, + { + "description": "Value", + "name": "Value", + "type": "color" + } + ], + "objectGroups": [] + }, + { + "fullName": "Fade out", + "functionType": "Action", + "name": "FadeOut", + "private": true, + "sentence": "_PARAM0_ fade out to _PARAM2_", + "events": [ + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [], + "actions": [ + { + "type": { + "value": "Tween::AddObjectColorTween" + }, + "parameters": [ + "Object", + "Tween", + "\"__ButtonColorTintTween.Fade\"", + "Value", + "FadeOutEasing", + "1000 * FadeOutDuration", + "no", + "yes" + ] + } + ] + } + ], + "parameters": [ + { + "description": "Object", + "name": "Object", + "supplementaryInformation": "Sprite", + "type": "object" + }, + { + "description": "Behavior", + "name": "Behavior", + "supplementaryInformation": "ButtonStates::ButtonColorTintTween", + "type": "behavior" + }, + { + "description": "Value", + "name": "Value", + "type": "color" + } + ], + "objectGroups": [] + }, + { + "description": "the idle state color tint of the object.", + "fullName": "Idle state color tint", + "functionType": "ExpressionAndCondition", + "group": "Button color tint tween", + "name": "IdleColorTint", + "sentence": "the idle state color tint", + "events": [ + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [], + "actions": [ + { + "type": { + "value": "SetReturnString" + }, + "parameters": [ + "IdleColorTint" + ] + } + ] + } + ], + "expressionType": { + "type": "color" + }, + "parameters": [ + { + "description": "Object", + "name": "Object", + "supplementaryInformation": "Sprite", + "type": "object" + }, + { + "description": "Behavior", + "name": "Behavior", + "supplementaryInformation": "ButtonStates::ButtonColorTintTween", + "type": "behavior" + } + ], + "objectGroups": [] + }, + { + "fullName": "", + "functionType": "ActionWithOperator", + "getterName": "IdleColorTint", + "group": "Button color tint tween", + "name": "SetIdleColorTint", + "sentence": "", + "events": [ + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [], + "actions": [ + { + "type": { + "value": "SetStringVariable" + }, + "parameters": [ + "IdleColorTint", + "=", + "Value" + ] + } + ] + } + ], + "parameters": [ + { + "description": "Object", + "name": "Object", + "supplementaryInformation": "Sprite", + "type": "object" + }, + { + "description": "Behavior", + "name": "Behavior", + "supplementaryInformation": "ButtonStates::ButtonColorTintTween", + "type": "behavior" + } + ], + "objectGroups": [] + }, + { + "description": "the focused state color tint of the object. The state is Focused when the button is hovered or held outside.", + "fullName": "Focused state color tint", + "functionType": "ExpressionAndCondition", + "group": "Button color tint tween", + "name": "FocusedColorTint", + "sentence": "the focused state color tint", + "events": [ + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [], + "actions": [ + { + "type": { + "value": "SetReturnString" + }, + "parameters": [ + "FocusedColorTint" + ] + } + ] + } + ], + "expressionType": { + "type": "color" + }, + "parameters": [ + { + "description": "Object", + "name": "Object", + "supplementaryInformation": "Sprite", + "type": "object" + }, + { + "description": "Behavior", + "name": "Behavior", + "supplementaryInformation": "ButtonStates::ButtonColorTintTween", + "type": "behavior" + } + ], + "objectGroups": [] + }, + { + "fullName": "", + "functionType": "ActionWithOperator", + "getterName": "FocusedColorTint", + "group": "Button color tint tween", + "name": "SetFocusedColorTint", + "sentence": "", + "events": [ + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [], + "actions": [ + { + "type": { + "value": "SetStringVariable" + }, + "parameters": [ + "FocusedColorTint", + "=", + "Value" + ] + } + ] + } + ], + "parameters": [ + { + "description": "Object", + "name": "Object", + "supplementaryInformation": "Sprite", + "type": "object" + }, + { + "description": "Behavior", + "name": "Behavior", + "supplementaryInformation": "ButtonStates::ButtonColorTintTween", + "type": "behavior" + } + ], + "objectGroups": [] + }, + { + "description": "the pressed state color tint of the object.", + "fullName": "Pressed state color tint", + "functionType": "ExpressionAndCondition", + "group": "Button color tint tween", + "name": "PressedColorTint", + "sentence": "the pressed state color tint", + "events": [ + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [], + "actions": [ + { + "type": { + "value": "SetReturnString" + }, + "parameters": [ + "PressedColorTint" + ] + } + ] + } + ], + "expressionType": { + "type": "color" + }, + "parameters": [ + { + "description": "Object", + "name": "Object", + "supplementaryInformation": "Sprite", + "type": "object" + }, + { + "description": "Behavior", + "name": "Behavior", + "supplementaryInformation": "ButtonStates::ButtonColorTintTween", + "type": "behavior" + } + ], + "objectGroups": [] + }, + { + "fullName": "", + "functionType": "ActionWithOperator", + "getterName": "PressedColorTint", + "group": "Button color tint tween", + "name": "SetPressedColorTint", + "sentence": "", + "events": [ + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [], + "actions": [ + { + "type": { + "value": "SetStringVariable" + }, + "parameters": [ + "PressedColorTint", + "=", + "Value" + ] + } + ] + } + ], + "parameters": [ + { + "description": "Object", + "name": "Object", + "supplementaryInformation": "Sprite", + "type": "object" + }, + { + "description": "Behavior", + "name": "Behavior", + "supplementaryInformation": "ButtonStates::ButtonColorTintTween", + "type": "behavior" + } + ], + "objectGroups": [] + }, + { + "description": "the fade-in duration of the object.", + "fullName": "Fade-in duration", + "functionType": "ExpressionAndCondition", + "group": "Button color tint tween", + "name": "FadeInDuration", + "sentence": "the fade-in duration", + "events": [ + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [], + "actions": [ + { + "type": { + "value": "SetReturnNumber" + }, + "parameters": [ + "FadeInDuration" + ] + } + ] + } + ], + "expressionType": { + "type": "expression" + }, + "parameters": [ + { + "description": "Object", + "name": "Object", + "supplementaryInformation": "Sprite", + "type": "object" + }, + { + "description": "Behavior", + "name": "Behavior", + "supplementaryInformation": "ButtonStates::ButtonColorTintTween", + "type": "behavior" + } + ], + "objectGroups": [] + }, + { + "fullName": "", + "functionType": "ActionWithOperator", + "getterName": "FadeInDuration", + "group": "Button color tint tween", + "name": "SetFadeInDuration", + "sentence": "", + "events": [ + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [], + "actions": [ + { + "type": { + "value": "SetNumberVariable" + }, + "parameters": [ + "FadeInDuration", + "=", + "Value" + ] + } + ] + } + ], + "parameters": [ + { + "description": "Object", + "name": "Object", + "supplementaryInformation": "Sprite", + "type": "object" + }, + { + "description": "Behavior", + "name": "Behavior", + "supplementaryInformation": "ButtonStates::ButtonColorTintTween", + "type": "behavior" + } + ], + "objectGroups": [] + }, + { + "description": "the fade-out duration of the object.", + "fullName": "Fade-out duration", + "functionType": "ExpressionAndCondition", + "group": "Button color tint tween", + "name": "FadeOutDuration", + "sentence": "the fade-out duration", + "events": [ + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [], + "actions": [ + { + "type": { + "value": "SetReturnNumber" + }, + "parameters": [ + "FadeOutDuration" + ] + } + ] + } + ], + "expressionType": { + "type": "expression" + }, + "parameters": [ + { + "description": "Object", + "name": "Object", + "supplementaryInformation": "Sprite", + "type": "object" + }, + { + "description": "Behavior", + "name": "Behavior", + "supplementaryInformation": "ButtonStates::ButtonColorTintTween", + "type": "behavior" + } + ], + "objectGroups": [] + }, + { + "fullName": "", + "functionType": "ActionWithOperator", + "getterName": "FadeOutDuration", + "group": "Button color tint tween", + "name": "SetFadeOutDuration", + "sentence": "", + "events": [ + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [], + "actions": [ + { + "type": { + "value": "SetNumberVariable" + }, + "parameters": [ + "FadeOutDuration", + "=", + "Value" + ] + } + ] + } + ], + "parameters": [ + { + "description": "Object", + "name": "Object", + "supplementaryInformation": "Sprite", + "type": "object" + }, + { + "description": "Behavior", + "name": "Behavior", + "supplementaryInformation": "ButtonStates::ButtonColorTintTween", + "type": "behavior" + } + ], + "objectGroups": [] + }, + { + "description": "the fade-in easing of the object.", + "fullName": "Fade-in easing", + "functionType": "ExpressionAndCondition", + "group": "Button color tint tween", + "name": "FadeInEasing", + "sentence": "the fade-in easing", + "events": [ + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [], + "actions": [ + { + "type": { + "value": "SetReturnString" + }, + "parameters": [ + "FadeInEasing" + ] + } + ] + } + ], + "expressionType": { + "supplementaryInformation": "[\"linear\",\"easeInQuad\",\"easeOutQuad\",\"easeInOutQuad\",\"easeInCubic\",\"easeOutCubic\",\"easeInOutCubic\",\"easeInQuart\",\"easeOutQuart\",\"easeInOutQuart\",\"easeInQuint\",\"easeOutQuint\",\"easeInOutQuint\",\"easeInOutSine\",\"easeInExpo\",\"easeOutExpo\",\"easeInOutExpo\",\"easeInCirc\",\"easeOutCirc\",\"easeInOutCirc\",\"easeOutBounce\",\"easeInBack\",\"easeOutBack\",\"easeInOutBack\",\"elastic\",\"swingFromTo\",\"swingFrom\",\"swingTo\",\"bounce\",\"bouncePast\",\"easeFromTo\",\"easeFrom\",\"easeTo\"]", + "type": "stringWithSelector" + }, + "parameters": [ + { + "description": "Object", + "name": "Object", + "supplementaryInformation": "Sprite", + "type": "object" + }, + { + "description": "Behavior", + "name": "Behavior", + "supplementaryInformation": "ButtonStates::ButtonColorTintTween", + "type": "behavior" + } + ], + "objectGroups": [] + }, + { + "fullName": "", + "functionType": "ActionWithOperator", + "getterName": "FadeInEasing", + "group": "Button color tint tween", + "name": "SetFadeInEasing", + "sentence": "", + "events": [ + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [], + "actions": [ + { + "type": { + "value": "SetStringVariable" + }, + "parameters": [ + "FadeInEasing", + "=", + "Value" + ] + } + ] + } + ], + "parameters": [ + { + "description": "Object", + "name": "Object", + "supplementaryInformation": "Sprite", + "type": "object" + }, + { + "description": "Behavior", + "name": "Behavior", + "supplementaryInformation": "ButtonStates::ButtonColorTintTween", + "type": "behavior" + } + ], + "objectGroups": [] + }, + { + "description": "the fade-out easing of the object.", + "fullName": "Fade-out easing", + "functionType": "ExpressionAndCondition", + "group": "Button color tint tween", + "name": "FadeOutEasing", + "sentence": "the fade-out easing", + "events": [ + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [], + "actions": [ + { + "type": { + "value": "SetReturnString" + }, + "parameters": [ + "FadeOutEasing" + ] + } + ] + } + ], + "expressionType": { + "supplementaryInformation": "[\"linear\",\"easeInQuad\",\"easeOutQuad\",\"easeInOutQuad\",\"easeInCubic\",\"easeOutCubic\",\"easeInOutCubic\",\"easeInQuart\",\"easeOutQuart\",\"easeInOutQuart\",\"easeInQuint\",\"easeOutQuint\",\"easeInOutQuint\",\"easeInOutSine\",\"easeInExpo\",\"easeOutExpo\",\"easeInOutExpo\",\"easeInCirc\",\"easeOutCirc\",\"easeInOutCirc\",\"easeOutBounce\",\"easeInBack\",\"easeOutBack\",\"easeInOutBack\",\"elastic\",\"swingFromTo\",\"swingFrom\",\"swingTo\",\"bounce\",\"bouncePast\",\"easeFromTo\",\"easeFrom\",\"easeTo\"]", + "type": "stringWithSelector" + }, + "parameters": [ + { + "description": "Object", + "name": "Object", + "supplementaryInformation": "Sprite", + "type": "object" + }, + { + "description": "Behavior", + "name": "Behavior", + "supplementaryInformation": "ButtonStates::ButtonColorTintTween", + "type": "behavior" + } + ], + "objectGroups": [] + }, + { + "fullName": "", + "functionType": "ActionWithOperator", + "getterName": "FadeOutEasing", + "group": "Button color tint tween", + "name": "SetFadeOutEasing", + "sentence": "", + "events": [ + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [], + "actions": [ + { + "type": { + "value": "SetStringVariable" + }, + "parameters": [ + "FadeOutEasing", + "=", + "Value" + ] + } + ] + } + ], + "parameters": [ + { + "description": "Object", + "name": "Object", + "supplementaryInformation": "Sprite", + "type": "object" + }, + { + "description": "Behavior", + "name": "Behavior", + "supplementaryInformation": "ButtonStates::ButtonColorTintTween", + "type": "behavior" + } + ], + "objectGroups": [] + } + ], + "eventsFunctionsFolderStructure": { + "folderName": "__ROOT", + "children": [ + { + "functionName": "onCreated" + }, + { + "functionName": "doStepPreEvents" + }, + { + "functionName": "FadeIn" + }, + { + "functionName": "FadeOut" + }, + { + "folderName": "Button color tint tween", + "children": [ + { + "functionName": "IdleColorTint" + }, + { + "functionName": "SetIdleColorTint" + }, + { + "functionName": "FocusedColorTint" + }, + { + "functionName": "SetFocusedColorTint" + }, + { + "functionName": "PressedColorTint" + }, + { + "functionName": "SetPressedColorTint" + }, + { + "functionName": "FadeInDuration" + }, + { + "functionName": "SetFadeInDuration" + }, + { + "functionName": "FadeOutDuration" + }, + { + "functionName": "SetFadeOutDuration" + }, + { + "functionName": "FadeInEasing" + }, + { + "functionName": "SetFadeInEasing" + }, + { + "functionName": "FadeOutEasing" + }, + { + "functionName": "SetFadeOutEasing" + } + ] + } + ] + }, + "propertyDescriptors": [ + { + "value": "", + "type": "Behavior", + "label": "Button states", + "extraInformation": [ + "ButtonStates::ButtonFSM" + ], + "choices": [], + "name": "ButtonFSM" + }, + { + "value": "", + "type": "Behavior", + "label": "Tween", + "extraInformation": [ + "Tween::TweenBehavior" + ], + "choices": [], + "name": "Tween" + }, + { + "value": "255;255;255", + "type": "Color", + "label": "Idle state color tint", + "group": "Color", + "name": "IdleColorTint" + }, + { + "value": "192;192;192", + "type": "Color", + "label": "Focused state color tint", + "description": "The state is Focused when the button is hovered or held outside.", + "group": "Color", + "name": "FocusedColorTint" + }, + { + "value": "64;64;64", + "type": "Color", + "label": "Pressed state color tint", + "group": "Color", + "name": "PressedColorTint" + }, + { + "value": "0.1", + "type": "Number", + "unit": "Second", + "label": "Fade-in duration", + "group": "Speed", + "name": "FadeInDuration" + }, + { + "value": "0.2", + "type": "Number", + "unit": "Second", + "label": "Fade-out duration", + "group": "Speed", + "name": "FadeOutDuration" + }, + { + "value": "easeInOutQuad", + "type": "Choice", + "label": "Fade-in easing", + "group": "Speed", + "choices": [ + { + "label": "linear", + "value": "linear" + }, + { + "label": "easeInQuad", + "value": "easeInQuad" + }, + { + "label": "easeOutQuad", + "value": "easeOutQuad" + }, + { + "label": "easeInOutQuad", + "value": "easeInOutQuad" + }, + { + "label": "easeInCubic", + "value": "easeInCubic" + }, + { + "label": "easeOutCubic", + "value": "easeOutCubic" + }, + { + "label": "easeInOutCubic", + "value": "easeInOutCubic" + }, + { + "label": "easeInQuart", + "value": "easeInQuart" + }, + { + "label": "easeOutQuart", + "value": "easeOutQuart" + }, + { + "label": "easeInOutQuart", + "value": "easeInOutQuart" + }, + { + "label": "easeInQuint", + "value": "easeInQuint" + }, + { + "label": "easeOutQuint", + "value": "easeOutQuint" + }, + { + "label": "easeInOutQuint", + "value": "easeInOutQuint" + }, + { + "label": "easeInOutSine", + "value": "easeInOutSine" + }, + { + "label": "easeInExpo", + "value": "easeInExpo" + }, + { + "label": "easeOutExpo", + "value": "easeOutExpo" + }, + { + "label": "easeInOutExpo", + "value": "easeInOutExpo" + }, + { + "label": "easeInCirc", + "value": "easeInCirc" + }, + { + "label": "easeOutCirc", + "value": "easeOutCirc" + }, + { + "label": "easeInOutCirc", + "value": "easeInOutCirc" + }, + { + "label": "easeOutBounce", + "value": "easeOutBounce" + }, + { + "label": "easeInBack", + "value": "easeInBack" + }, + { + "label": "easeOutBack", + "value": "easeOutBack" + }, + { + "label": "easeInOutBack", + "value": "easeInOutBack" + }, + { + "label": "elastic", + "value": "elastic" + }, + { + "label": "swingFromTo", + "value": "swingFromTo" + }, + { + "label": "swingFrom", + "value": "swingFrom" + }, + { + "label": "swingTo", + "value": "swingTo" + }, + { + "label": "bounce", + "value": "bounce" + }, + { + "label": "bouncePast", + "value": "bouncePast" + }, + { + "label": "easeFromTo", + "value": "easeFromTo" + }, + { + "label": "easeFrom", + "value": "easeFrom" + }, + { + "label": "easeTo", + "value": "easeTo" + } + ], + "name": "FadeInEasing" + }, + { + "value": "easeInOutQuad", + "type": "Choice", + "label": "Fade-out easing", + "group": "Speed", + "choices": [ + { + "label": "linear", + "value": "linear" + }, + { + "label": "easeInQuad", + "value": "easeInQuad" + }, + { + "label": "easeOutQuad", + "value": "easeOutQuad" + }, + { + "label": "easeInOutQuad", + "value": "easeInOutQuad" + }, + { + "label": "easeInCubic", + "value": "easeInCubic" + }, + { + "label": "easeOutCubic", + "value": "easeOutCubic" + }, + { + "label": "easeInOutCubic", + "value": "easeInOutCubic" + }, + { + "label": "easeInQuart", + "value": "easeInQuart" + }, + { + "label": "easeOutQuart", + "value": "easeOutQuart" + }, + { + "label": "easeInOutQuart", + "value": "easeInOutQuart" + }, + { + "label": "easeInQuint", + "value": "easeInQuint" + }, + { + "label": "easeOutQuint", + "value": "easeOutQuint" + }, + { + "label": "easeInOutQuint", + "value": "easeInOutQuint" + }, + { + "label": "easeInOutSine", + "value": "easeInOutSine" + }, + { + "label": "easeInExpo", + "value": "easeInExpo" + }, + { + "label": "easeOutExpo", + "value": "easeOutExpo" + }, + { + "label": "easeInOutExpo", + "value": "easeInOutExpo" + }, + { + "label": "easeInCirc", + "value": "easeInCirc" + }, + { + "label": "easeOutCirc", + "value": "easeOutCirc" + }, + { + "label": "easeInOutCirc", + "value": "easeInOutCirc" + }, + { + "label": "easeOutBounce", + "value": "easeOutBounce" + }, + { + "label": "easeInBack", + "value": "easeInBack" + }, + { + "label": "easeOutBack", + "value": "easeOutBack" + }, + { + "label": "easeInOutBack", + "value": "easeInOutBack" + }, + { + "label": "elastic", + "value": "elastic" + }, + { + "label": "swingFromTo", + "value": "swingFromTo" + }, + { + "label": "swingFrom", + "value": "swingFrom" + }, + { + "label": "swingTo", + "value": "swingTo" + }, + { + "label": "bounce", + "value": "bounce" + }, + { + "label": "bouncePast", + "value": "bouncePast" + }, + { + "label": "easeFromTo", + "value": "easeFromTo" + }, + { + "label": "easeFrom", + "value": "easeFrom" + }, + { + "label": "easeTo", + "value": "easeTo" + } + ], + "name": "FadeOutEasing" + }, + { + "value": "Idle", + "type": "Choice", + "label": "", + "choices": [ + { + "label": "Idle", + "value": "Idle" + }, + { + "label": "Focused", + "value": "Focused" + }, + { + "label": "Pressed", + "value": "Pressed" + } + ], + "hidden": true, + "name": "PreviousState" + } + ], + "propertiesFolderStructure": { + "folderName": "__ROOT", + "children": [ + { + "propertyName": "ButtonFSM" + }, + { + "propertyName": "Tween" + }, + { + "folderName": "Color", + "children": [ + { + "propertyName": "IdleColorTint" + }, + { + "propertyName": "FocusedColorTint" + }, + { + "propertyName": "PressedColorTint" + } + ] + }, + { + "folderName": "Speed", + "children": [ + { + "propertyName": "FadeInDuration" + }, + { + "propertyName": "FadeOutDuration" + }, + { + "propertyName": "FadeInEasing" + }, + { + "propertyName": "FadeOutEasing" + } + ] + }, + { + "propertyName": "PreviousState" + } + ] + } + } + ], + "eventsBasedObjects": [] } ], "externalLayouts": [] diff --git a/examples/starting-first-person-shooter-horror/assets/Flat dark joystick border.png b/examples/starting-first-person-shooter-horror/assets/Flat dark joystick border.png index 606bf85b223d68dbdd1be4f5ac851f2018e13828..b4e83e112eb9375cefbb86a6d044a972f8bf4dcf 100644 GIT binary patch literal 3751 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 delta 928 zcmV;R17G~79jgbB8Gi!+006uZmmdHC02@$DR7Ff_aWyqHHa0djH8nOiHZ3hJHa0dm zI5|R^?&H|1(F!EEFDZlRnwi_ z{vfi zj=HzjIAJ?^*aIR1yr3o5f0`Hd%5^<6F6Up zP~Fjl;*KVq#|bI9VKHMl8{r`)iPZ%{-xG$jf4=?q~^F0)IA24NSmi2{R2agHyOAVdeoQ zxPbQ(79!wkIWZ#NI!%xsUi&yP0Snv1t6i5;0fp`1zdpu#*d3k2|9q@mKxK#E_j(Kt z!LJ|`GN7)T^niore1Z;j$NmwypId1iKp=wpMU(OX1$5-Pn-+2DQ3 z3U8HfoorJ0VFQ$enSS6!7WRpkSLewMoFS+@5$ws+?STP?b}+=r0-O&JIbb}yTqpDq z_kD-lXD%rF#snxfL8d2)U`M4yN4i*I^UWcQjl?nfK%C*3a$s;&f%6R|JUAMx!11?+{;EmT?0T5AdBGkQj7xl&{aQ(}c)V~^X*e%3^Jziw;PzI z-Q)Pxrd*^ diff --git a/examples/starting-first-person-shooter-horror/assets/Flat dark joystick thumb.png b/examples/starting-first-person-shooter-horror/assets/Flat dark joystick thumb.png index acd005cc067032996264e510b58c8dbc0c102b62..8acf179f8660be36ccea1198105da35c07154fd7 100644 GIT binary patch literal 2939 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 diff --git a/examples/starting-first-person-shooter-horror/assets/StarterToggle_Keyboard.png b/examples/starting-first-person-shooter-horror/assets/StarterToggle_Keyboard.png index 54eb525bd31884a3c9174fd55987b5d12d415c2b..aabe87250073b2858bdb31fa561d3cd73a5c2456 100644 GIT binary patch literal 3413 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 delta 1267 zcmVk0APj~}1qIg|y;$4-|3eSdR=EXR#Ljv0&8i#El0bNq zCCPtbOc?zjLt;Ha9GISu$7A+MCe}mfMjVETgcv#JMftk%V+^MpZhzV1`Lv#fQTY|# zmVH%Kc?7GE@qgm|MuXXE5Wx|LA|lGXymX>j0mHBaiOCTeWuiGqm?tdkJb~nKEi%ds zbM!h!xMXZ&gS-Jn!ieZO!|zk!l0H6(3xx%l_7e^=V*nBnTyJoR3x(m7Y_~MkS+K|$ z43KnXp6`KzQc6$TjZ&Q@kr{*CB>m&JS3P*(6`NzwM1R5L6lTO&ICHWPon+|X`e7_z zMLC6F0kUhTIljIC`n@PVwP`u5k8%6kln44E_qKxh( zb{b06LXp^Yg%^cvtZ<1NCHc|QMWFQ-3U_9ArK2>SoN9MLNe(uuyLk@p)7%> zJlGHAXO1vp$Jbv&St9IU$JcM8FoX~@Q3#uw_NohXG+4jH|n z0sTU zpp0)pVW8-j`U)3UIhi+}#$BQ8($xh<8D1zSF{H#gGk+;3T}A$ClUFXto{2|3#opr< z1{e9RtJiwXxPn1_o!Y>j4vgs*Rnv1zdY19I z1T&QU(iw}qhn0M_M07d@(=B?wrnEYRR?F(SFg8$}3iw!f8+gbg+55IH7m6mdx_{;G zwJ5GVf)}rJh!K6BOpyHTY)Y&~5puDyja~ZueJI8j_G9+&*0hR%T$yseRbcqgywvf)bNZ*(}g~01Yys?kkB1~sKZ8{!H`|Y zbwYLk4a7gL^N~YT3{4p$1wFQ5i0U&(+omc9F}7{A?VAP;ziABgtA=yGYGnGW2HC&s d-wXbp{R5(SZUFku=BNMw002ovPDHLkV1n@6OJ4v0 diff --git a/examples/starting-first-person-shooter-horror/assets/StarterToggle_Touch.png b/examples/starting-first-person-shooter-horror/assets/StarterToggle_Touch.png index b38bab4da6aa9be1bf4487046f521efd39f403b6..204462798cece3a787bf29aaf3c77d2867382756 100644 GIT binary patch literal 3329 zcmV+c4gT_pP)Mx*Y;j!XU|U>?Oe|6YNhj^?{dC##zPrsY-|j9z25B} zkFOz-h_5G&#szpho~Zcf5qRWvbrbTg{G$FpAjW~Ep|hs(YO|m5X|^*rV@Nvdie0+g>;Q3S(Hd*^&f;t-mq(2U0qF@{P6hk z%p5r?L1@nyZN;*RNmyiQSbHp+q7P5s!G^R?jV5SWT_3x6tEsZG zk~I0@jorIVvCOGhNa1CFB29jPP@3;dCBD|@+NMc#rT_a!zQW!P?HRT&`0utZr%|ClQTC>DH~=6b{!@D5Nvi`2>PkS?{M+^%X)S z_M-UgNu2I|GER4Hk5QM{N}14Cnu+>SPOZ1LF@=&yB#-TVvnEH69;d;7Nz^Wk27#n?*wvFHJWHCfC zGBP5!bk+hQ;dYwb0(a`Q)}XW`%*b$p`X0ok#lSwq0N53KKIw|_@ut=Qy|}J|PMsK$ zmd0)P$pOHvI&|o;5J602*w)BRQy5?oUK5?1;|>U-vtyL52nzv$q~prviTl|KK`=jU ztE7LNe@cHpA0s#62%+4+e_x6OeDJ})sH&=p8XCf+$uAp32Y>n1Ip#4sw<-{bhXZ(s z06<8vH2q>@QDJ*YSQC67?&0rrlUN)fl;z8pQ%%h?G%#TJh-c2cCtR>9s;#Xx+tk)R zN4>rGsY;yJ%${$on`3rp2;j%$&Sn%KRh84#20Y07-X@~$>D0L@d|{2*ktK4zZ8VA?=uAlv9!HeDZldv zKMK4%saT0&W+8mD8QlMsuYg(@{1V^J6u z%PrBN!3Wv4Z>B-kfJ%hgjHR z@bBO^I0=Aqm{O*w7KO`FR1ib#+O^X>hHx%koVr6pBwIFrNA9yVno3--EXatdX=pbC z_X!vsIG%jc8yy%HfJfh98PvD}?Kz%-7~qP%B--F!xLjvXkI3@~{yD9T&8P(%H7yN( zuTUyH== zVO*sYDXLKHf_9ZcrEr?`~^PqyWN7L|IOXIcc~DdG^QNfuj#;X4BRujaS)C}R6zk29e)ms>+G@(4^zqF zSfTXy4`e-{2)Is+8MMisW#Z$HyUER{LA2G9s89ds6YkWBN!P4wP{w2!#4e~CJ9|22 z{%-sJq2$<#P7mjtSZsRpPLSb3xe7q03_<#Iux8)f2B&EO1`a{=NorXN;Q1#kkUJUb zw0O+KN>}nbCVo3^J4a=r6ee@=r4w+hP^Q~)F@SjTre*|kM)B)_Q631COj5by*cMxQ0dH z;zc~wVF6e?=T1+!qp&LJEC*W^d=D(fY+??l7En4?D5%%Ure@2Iwzi$5nT)l}1f3Gc z$WkF@;K^1H62xK+$YE8|i3Rsz6n?gWIhW;Ua`%dXV}*iAS9iM$GmZiLR>iSGc_f6AtFroRfb}W| zp`i1fIx%dPzaxNFXC|`i#B)mkTsEEw$rdS}IMJ%;V7?6DYG7cUnTUZwV=~sn(;UJ1 zqGaW9p`P?=7S0LiV7^RIiyc`kILE81VB^dc;8Aro&dDcEvntx6lricoMRv8|lCd~o zdCs%mYY@4LeWSN$YX#J%M@7`+E$AvnR2r#I+)j_AeSNPh^MUArIyjFz^WADiSLAQ z6>U+Hg!G!mwxkmYC?~7pSfK<0gu6UPdrW>F0llBz0gQD#CRtx7TH zt2kCDa#8LAxPWM=7aVuNPiup%q_SVZIP`x*%}JJ5+|)2=$$h0I>@Kd@MGBX~MIOWB7fLP7?@Ivem;#>0*lx4pMJVuUw=X3v-XeVp0r& zU{J2CQd{?&6pALdSgVYUMeqDlIMd0Hu>dnIu;&D9g)%fWM5Tf(bMF#_peGi_} zmE;IL&$mzKs=;#b+aY)?bai#n%9SfgqnQc30;LKUH5NOQ zW@RXL&vGWwV{K!~JdsG;R4-h+ew{R$T*B|BcUl4EqyL>tIksv6m_qS-J%3d%T)1#S zS{04P%|t)HN{u^6-lFqDFdB^;8F+G*?mcX`oJ}0L zapOjhwQWjHud%VQ&gb+0N9=~w^VQYW)YQ~O^C~MzlOOOv;v|aCH;-!OZz4^87>^Ip zHo z<8qRgq&1yLK*r(JNF=gdG})^Kqj4h;h)81nd2DR#fY3W$JZcuhh>BiZck%| z215bQVSi*p(dsqGsng0XlcP6^qcIK!$|x{; zkj`ncH_8k%(0>w&lVpVM8NWY@g|R7M%%c!13GGjE3d%HDVHPJQMj20$1V!9qe>(k8@C`0e zfK|jHiXs|wufS2sC^Wk{N^#Qcni&FJoIRVAJa)T_& z#E(d`7p3ME8Gj=zs>&l7EXcym(wWK1(i_Fqk3%G{UMq<9viM*lD!R^>2eLzXx&7rl|9b z**0(+%KT?n`P0JlHbu@nszZTIQIP&hQ53g1QRu+Pym^$f^wxjpPYISZFgH#L()CIU zl2jC~Tm1ak(#sgFm>94|!IKy*Ht~Fj& zQHv7n;%r~6Gm0!VB`RA*_*q#C!{A`ETh@!RY84%B;K8>jX6w#>GfK8HUf(P0242!u z-P4PNd36~H+KkMHM{@*`+`~gCa$gv0fA^iXV002ovPDHLkV1l+s{uuxO diff --git a/examples/starting-first-person-shooter-horror/assets/StartingCollisionArea.png b/examples/starting-first-person-shooter-horror/assets/StartingCollisionArea.png index 0cf1020205023234d56757be2c4eed0e05d7002d..443292ad3a56caa45af6177c5c49bc749794facf 100644 GIT binary patch delta 753 zcmV|gm<#igRwFyugrlh>ilg7#fNLFgUk6GgZNLK0~ zJBHCCAX%w{E?C9_kZhGP22m-sFPiYXZ7s|5%ERJZjLM041+}XuyfhQl&Kkag_&CCqPo3 zqft(r0BL!SMmcr@{{Og$6V66CG5~3L&PF*DfD7`R3)a9LkTiMF3m}Q|pc_C^}5GzPi00000NkvXXu0mjfQ3_BV delta 817 zcmV-11J3-22FC`FBYy#fX+uL$Nkc;*aB^>EX>4Tx04R}tkvmAkP!xv$rb<;R3U&~2 z$WWau_(B||ibb$c+6t{YnB4RQO&XFE7e~Rh;NWAi>fqw6tAnc`2tGjE1t&!pDe>H- z&?3eIm;3SG^Z(p?E+Ev)Of@^k0ade%WGpIXva4d?72Ozs4}blbkeI2*Q;TVM*4I6C zQr$&(mUrKu)vM%82KYqcIi?#H@dokCrloV!6?xAV%r}h zKwuYW)NK3v*tQ!dfd3h|(pvsX9hm(jz1GsgM?l{;aBU!s02y>eSad^gZEa<4bO1wgWnpw>WFU8GbZ8()Nlj2!fese{00ACJL_t(| z+U?pw4u66$6olciVdKR(mk06$p3Q|j3<0DqZGG>}uyRMfKN2*Z+w+dcJYo3tdVfFv z^Ztn$4*-c7KTm@LSi=wf23W)o3t$yLB!Fdn_W;)MT?2T)cMISJ-z9)2eDeU_@J$1F z#5W7z72hO)XZ+;=-tm_LM8H=Eh=Q*Q5D8x#Ab%RZC_qGfc7Uk(tN@Ymrvakle+FQ{ z9|mB-?*?GPZvwF4R{Go>JeZ)B~HwEK>Gmg1GEod#t#dieSqOtt@Z)T z_-+BT4{-GJTKfQIe6s-B2RQyqt$hG9zB+*R0YUJ^0kjVYhR+V5eLztBX#nj5g5wVZ vXeu8-g5LztK7b6r1kgT!6u)`|SWk&R!a@PWh3-$;00000NkvXXu0mjfQ@3cv diff --git a/examples/starting-first-person-shooter-horror/assets/StartingGround.png b/examples/starting-first-person-shooter-horror/assets/StartingGround.png index 1c66f93412b0f53e8c73213298b347157759734a..edec42fec83b5ace1e1abd51c617a430c9a319be 100644 GIT binary patch literal 2824 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 delta 4495 zcmY+Ic{~%~|G?35%S|dr48@!ssF*Z4Gr2NX5{9`qWXYH#rOT0X4AGpCGvv$?W6Erf zzKF0`XpXX4{MPrM-|zRw^Lnn&^L$>P*Yn5w{d&@5N>rZ7>PPeQa&d9p{6Krh=>)

CVi05h7CM;*%6n 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 literal 378 zcmeAS@N?(olHy`uVBq!ia0vp^4Is?H1SEZ8zRdwrY)RhkE({(F?hNicYo9j)MK}vQ zB8wRqxP?KOkzv*x37{Z*iKnkC`ztmf9$D`E|670}jO?B+jv*Dd-rn8F*=iut7C2Q$ zP`v9mf8wv||V~00!*A{PeEu8xM;`w=bRd-)koZnsVy{m5C+^YZ2 z1Uh(uCNVKMFfeccO=M7DU|<0`QGhJLAMywO=-=p++8q2|e`Wn)>BE9c@^{)RRP6uF v-1ObLsfL*vmO$Mo$MBAy5`hE>kEmfl z5)%T1=o4w0TvNwAY1pAvg+1$PRIQ!fG>-^8-5}AhxY=gy8#SWANmpoPS zRVkP|cWyy_eSN;(VxRu3j?w4jEnd7B+r;YCtK~KCz4u;yAOGH-=LPFEE)P3qaODKU z3tv}Nnr^)DMysKr!P~ocuXy&^XZ?p_La4vLe=;Qs8?C(jM&Y43WE0$RiBx^%(4@!_}`^Mkrfww-)8>%rYpQ{cJkbt2M-=Zy<}m@^zZ_4 zoFdax=jeLILfk6|RiX2U3KXlVZqYJ}+?B$z*7DJlXnb)LWI9gj_lFPc+ryt->|`Qc z)&NQ3h7B9M0D0apS`-Pf2~=-Va0UHqaYZe*SC*4kveYJbJ*15;03eyn)9)yG_OD^7 zsAW%%7UR7RNe)&)Ni0{!w z0FT`J+V{6?;z+>Vw{IUevpxAtFDu>5Ct`UHWU@XaFC{}eX?9ZD1D2EW$-SeYjc zwKi0ts@mF0VcS2UPZ&5>-0awPC8ZsU&qshG2}>JBRWg#FU0q$aWfi`9QHLsH1&276 zDERxdX_b>n@u57k{MOGc`iHA6A4Do-VUOE*k03Gb;R+`~>8dLi)0K-qOBEF}sjjw` z_m#-~Kwn=Ub#@-3Q@m|!J3za3|A%(&+Aa5wH$36~zI|WIIvNVJwJJ+9A)wae*8>+Y1&W&)7TNB;B}J@@msx{rS%}(6i5MHF{kQHe=UR z&TUw#LL1Ua2WW1#p0fnEHujs&=dJzb*Qx2&n}=0ZND)-A?Z5$L-%k5B4(EWFii!%l zitXgJEWxveAqwX}8n-iVqTgTzN3eVj}pl}vs4$icna`CkwyNz}wiyGK%e z&Dy@<$b*#Kvtgr@yf}blHr;Y_Rw7N4sBGaERU%e86iFH>^{uR|zrxNlmu1OD9#rf% zzxxB-x9NVVrg4F5Y~S$)ojQG*7A;&r1qB8EckuE6!X6V~i#Mli(zLfcXW(decQb9_`tZP_m%JV+_(1RJ?77~C5i)-(q0QdO8o$E4MDX7cO zH*O#!;3i(8q--hM!1VA76*x1KiIrpkFy?4k1OY=n5#(t=%KsR09#CZj@Fo!CN{Zp^ z*|X8_W<-7A5raUOvSsVvXri%|?->q_(E2d}ZUBQTr2Az2S1@(XobpMRP3okWxNiA! zdid9yGxmLX#j2d750bKK<@Iu_?G_}el?%LWF6~~N|H_NoGWLDvrdxB4IkO>ocCg~; z(fwTSmiMDZdrfK{tjWPFD7MI}!+^H5+0f<_-fGxnhg2tuMd1l${S2m+y%35X&K z(TcFx`+9aFe&_;29kwxBo{pxvAPDVP9k-1HkcVSyLw0sJgcA&7&HRL6dv+st%ByZl zE3uGb;;O}&506kHO2`62o?xlV<)Lsa&WR$4P}k05$1=`E-Z3t?wYG@U{pm1<9Z2l( z<;p8FE44kMt$O0|N5>=)xr8RKzrG{ma}-IBfHF3Js!znDGKz_C=(00{p@!3m6X41$ zb_6nz;R(V>P%XShG(Kb)!-y?`VJQl#&2YH(+GP|MH>|vV)MLUg6I}-Jk?z6NEs@v~ z7=Ei!yl-7ZM=%nisAD*3f&LZ)m@{+c%;*CwM?+WwqjdPx8Xz!=I)>|F!V7BzN7DZv zIpP?bDG+F6&cs;Wauho`8d@WSN@LNEDB$#d+KF(cwS!`!?{vt{l#Fg?cJ7Sdq>37A z2JwN3#D+(`$B!R(EjEOn@+h&;+S-=!If`*N(G3};Z9HpcMaE|n66gsjbi#rjbI(!> z#YEWaMsW~mCB$jiLz0kS7!Bbd-%L_h^y06yfv)-x2fKD<_B%w)y{+etrLjnaPSQp} zxGp;lWMf!okMhP#H|4xkGsVQt-I-%mV0WUl5+J!qG37!cj>S1qVohj5*an*DWb!;$ zawZ9MQeJEV`_#anAfmJq0Gzk_>uVwzcSsM8jjD#|I}QRi(}WF7r5u<&*)Lo;^;4fJ zpHx!*0mTF)4q1{Qa!shBSM9px%cSJ!mI}@Xak+NDXG^Hw@xyhI?B;0b`LKJ|$&>GO z%ZxOa#N4{Ze{rigc3SIYu5fVJ1s?pntZo+q$kqfq+#%s#;TIYg9QL)G?mqr`jxSTv zi6E45J$j1_;DHAp%J__M27Sa64cs$4S^*;!Ou+NRn;Zjrm;ovYLS zork_8Y;85uTuxoRylhI+w(T`!02r&nPAQ{F2dRKv8XOofH&#UfHt)WlZJ?=Br)2C0 zc~AW1Niv0GaW6SvoJ&f*$!=Y4OQJitl0_ZAh{d)|Nl zeJOqLAHe~Za(FMLr#I|nDf~1U#2O~+Hty6OZHO6a zBMtFo^2`q>@jCjtkJoef0`gGgM8s?>3nk{%)Gy1+&wGgsL#tceIE6H(M!jZ(n{t{C zz5C8nZ0Z~}{ma$U9ArwLoe36Vuvv|57E!CFw(%8Vi6t}+SjCP6>Nm@fM~1W_fREZ2 zohv+#TH0Ipe1ZQ>(UA1XWmEhqo#@+C^F-vN<2f~TSLEdv{FBBJX!qW*a>ba2ok3NR z&WF017%Vgr2G5*X(9v=5P3tI2lqEG;9P;jkpk3L&tR8Z$r(|`RR5qyMt_N&S3J2Y zKQ9le9J}-VB17+BGI@3K%twcdi;Erh4Gx<(SIztP7ccXtyQ*pJS~R7RpFMSCk(FZ{&JwN>j+Dpi>%AEY16>pJ!eZlR?xsivW!!O`n%h1Vbqk4o?t+95UNpPh+-J><`c>e>arvMjraE@8N? z_s2tR&38d%Ih=zg4#j*!>n8XJkBtd_=~Y)(yG2DsTBTEm+TOW~b2eM)5(Q_YwlaAz zSFR#YIt!?-!g>AYS;xf$miX|SdI)Veg(D{0L_$d($kTzc-&_kU1Epi; z?2ArlXah-U_T`&k@^l>J^mQM*%k|v*=@J2+B|x4oJ_mAM@Q~*nQH6bIV~OQ5No5{L zy&3k-&Y{+pP3);~QzKdI3xr;dFq-&TenEjITHskT$jY{w+pq-cReH2EA_meIhJApM zJ=E6nhrZLNmzWrwG|^xIX&KGO6nHE{RY-md18Cr|n+OX-Da5${RzbSop9 z%?l&y5}-6ppe+klfjoEa97vQzPMTJ+Mp)L}rjxQ`1x!q8AlY>E(Ehh6VCz@_JdSaL z?OUQDyC*2Z;M1r}fCg6s*AcnD5RD2t9gZ0t?ixV6_!O)4LN?I1A(uuM2vXt(23Mdi zOdh&z{d^ub5?n=I(BAoLr&0v+fSd-I2NSRX`v30yFLS0Toq={_ZbM@fh zxg~t^(-Y)u*t2?%xDFH7*8Ei_?hQ&4mqIp~iIa6iBY}p(6<0lvh_di(5U~cB>qKne zI!K4frIE2#KI|iAcO%11Dj=_(H-8CO^$R})CN&C{O2OF;YwFagf{nPSsHiC` zEY5EvJF(Crv5>F&Y0R49E5LSUq|egQD@UTVu$Vx~TAWm>xv!_UrMtTmKSqFz32_1d zCdK2ICM63m5?N%WCE;a`ecPU^aa@Tyw;0#KHPyA{PiEj}GFTgAFC!`G51OEn)UBYy-%NklAs8wZPv)FhF*g6H`)a`Z?7ZYn?O@FLiuh)MC1sXP#Zf=y$ zgDb!c0Nvu9AyC^q-N=OtxaWy?Lp*#Go8!xZ0(^lcoA@zH4J26AH^rGyWGUrjqtW=| zQYkRV$P>PEDZopA)#axEm&4ZIzP0P&Q^6D1`oPZ8Apa1+6G3ES=^J>LP{0!v3^|A} z140B(1d+_t34cZAd69`#0gntsIOP;a0UJ5o5^yCpuMuPk!gdNUIa`Yl+`MZ2$Xs%6$@2mXYgz(jDKq%!GiI4TnM;4Ah8x_u)(|X z02{g%CKeSIa@>s6}^hQpzDE!?A1K6n@2Nx7#i3B@JW9kESsVGT&Hjffz&*D3LEPyh* zEl+Xi6hhJG@f|)EKpXZoHIikZsMxp?0NRcSV1ER#IDmP+>boqg^0mnTq%IDZX+};Z zBC|JL6swaP(za%dF5sXF6mXj{nITOb3sf1>c?K~eEh<|OpXEhtR2~nNA)UnpsZ*J% zjmi8}P#0ort<##EiFL}HFlTP~rAmvlw1c85fwMZ{rL@CGbpl0#ovY9&x7-&c zTio?AI&8=?Z=BMM{St`! zCWP86hP8)GRX`+dz!O@_Coe6aLY0ZEO~^t6(Mtz6yf6e42gxC?9WBe=_EXcKwtpO* zeq#$zhfQc>+fO&9&bo}j(9%(zYTSC{uOo17x?WB>70s=;{xrD0`nR6?OA3X9H6eqW z&E~&a6~>J+N5a3sIN5-8|F;-e!)J^4|6$(dg})1Y=l}o!Ep$a#bW?9;ba!ELWdLG% zE@EtNZ)9Y7E@N_eaCC1jX>DO=WgRY1PDe5{MQ&qnWMy)w27m4V0000l|q5$ zM_V-s{Y8`(QdOyvhA8Qewh3uMN>D&1`V_3mA4_LIi5d-p!(e)r5dXU^O?21q}cr}h@Ei{&W+ zEP1-*>r$|A;lhHZrlx#T(>C*6?_-Y1Te@^9)q&NkSF3YA_0&`5I_|Zll-_n_L{|rH4E@MlVVTlO|9 zNoW#XQXxg~M0Yd_7}Nt|)D@^^sH~0E3LOt0hknj{X+kzkffkYiRi{TGlN- zSk}xyLP$zyb64lVUqVa@jU*C@%6d=a>WnIZMh6gS=4p_zFvo;KAzoL%_({?$Q6EIDHH@Km$sbwqZAttFO zVuxs%$iUI93;SonsvUFgoucnC4)TKZO3`1~QWP zm6O-O7<0Wih|`N+vmGyi+_kp#i+@PU7Oo?p5x$?<18?CF(193n!uzxNz(*c5JlfC& zYU=B&nPoi=*BQ`PY;!ED8e)dU<-@~~0ym0Lml46w-rin|^TPM0w5yUT(8b}b;Exp* z)w4nKp)Ire)ey-&-PX7(?kTLInH#R9F|FUAc;(EBQgYWuj8yb*vC0!zR6CHrhhs#3 zvEQyOE-E?S7Z=ajpwV-`Z>%4kBsons1N(+XMH*+V9*A*#L18C@do>^g7baxGYqYzk>v}{wrJE zm&keaksnXE-@W%e0Q>gs2fu*>x{tdKw|`9sVwFKL#HqTvy6L7sQQkix3!pB$^Y?Ej zv@hyvSbg^zIDPsI_zfrOeBFYhScWjxB~R6UF)_0g>lq@(hB(F zy3o!2Vp|(z3S^m6vFNG5l2$8D%Ij%L9%z9K!?-Jn`g)VO4q5h0UkB~Q5_G0xZyGR= z8FgR$lnes7H&1W+8Dtn`sQ$DcD4`582)Y@+$TJ}KD5E|ildY)!lmt?oKVuR|?#g6+ zy;MO_oy>*6VpAbzB$5#cq{dSSDx09|ue$RN$N(11n>R1<7c7$nNnN(dGg$w~)dAXc z!TTnC+Cciw2PK{`$cRm5%sm#)oH?TiOxv0X13%n|WI^Pm+o)7lR>q77Mp}CyjmT7Y znJgOW>mhBB5ITGItl+2!rM4((!;#KUC)|ey|9f%Vx_6ReS2{k?@$dgmr*F6~nG!k{ zpnklttRe_$bY)(BeY+Z$#zWmKhMlPHM3RX_veah!G8GVcbUjiTB>A_kTmd)Z`5NMS z0bT3Kjhi5SU?KWTi~Xd)FX=SLAAYzSzH$31jJm{T*FM<&kxN*nKem}5!Bc%Tem^BK!nnRq>>3pDcY%4g6G(>9dbwt zw(SJ5lFE&_90}(LOcGZ@VOq!-^8+uOJQ+fn3_=bG0o}f!Wko_54~$75xiUh+$B;up zkdo~|A5hW?P(>iRGSac_KSK@)!HGuy0{We4S|_Gn7VPfo*n@Qla+mKa=QF<>`>51=tC#pzNBD9{*tOWO6oZ#99uPvl0@pdPpFTG<6h46;mA9ffa^cN?q!z_eE)~kgfje9C=PxQRC@K6a zZqE3-GuI|$&(Xu}i_!8rINY^icm=zv6=wh=mil`wGR>D*dBwA9^7HbjD`)iek82R2 zNHltD^vcEK#l^)A+6D)uPv~|3Zt^mBxvO5*Zqmi66d(l!1)}F@#|a^v$KX1H5aPL^ zaPJ9}JTe@zqM}0SJ)MNZs681kfdsE>gWY((H8eD2mCl~`IJekO;W_~6y>RcDXV0HM zZRsyE`o%xz+*$|I7-Tq)b+)gkE_>?K zDYcjew9n_so8TgTGS2&>tgWpTMMXuXOWVgf53a|UO=8Q5rXVmH^)`}6>Pl$xl(FzE zLcePMeAM@1JSjfhp&q(6v?N01j;XD0eV%jnAWS1@$s_X2FwGsVdD1|c1?r+Bpm z65NcIiY_{pqu3$zFxKI{JPX4TCp`B$(rF$go7W1@a6F3 z%gfUJa*`iF0SOLa+flFYzNRArVEgTQWrdXvg>#k zdh5cOLS0(OS1+EsaQjJYJAQKt3k%hKQKDLisV&oWU>d?&;$w7c}qh@8{?z z@@O<_Sw&?FN{Wl$xmJgM6k&?mLyz@`dfP8txZrkKh()8w-&5q7QO0OGFCK}e0EICe zZB-}>4P!cy3xt-dA!|2aQ!bT)W5vY|JH&^A(kQ}a|YHk@TNfp98z95zQt5`&a+~002ovPDHLk FV1j1w)3N{n delta 627 zcmV-(0*w8$9DxOp8Gi!+006nq0-pc?05(ueR7Ff_aWyqHHa0dmI5;&mH8L_XHa0dg zGBP$cHa0dk|NsAAUtjX_^4{Lw#l^)&Mn->se~*ukx&LcZ0000AbW%=J0B>IZjxp~R zy4HO*rZ@lq010qNS#tmY3labT3lag+-G2N400G-cL_t(|+JD8F>ypAC4227X5~x;v z|ChDy&bnGlZe(`P@6Ii$Vi%H32h?z{` zA&({os!ck)jd&rN1@*kv&X;~w(7+>zIGK0^u?M`ITYobVd%(4ET6Mt2>z{${OJ<6^ zsnagq<8`6Sd|rA-EH^URbgQvHQ^O|E%x{@$LOs$9eVLZ5Rh; z*XQ!VgKQZc-=f2o!?Ed*2lmdfbz>*?te|XMb>FPA<&m<}EJWI#E9%{YoXZKS(#-Mi zEz^go>*bgJ1}U?hv%<;dkt!VPW>+x%4v0{XX>l^EM0ovUQ4t~&+a0mURTHt&FNy#F N002ovPDHLkV1nr&BUJzZ diff --git a/examples/starting-first-person-shooter-horror/starting-first-person-shooter-horror.json b/examples/starting-first-person-shooter-horror/starting-first-person-shooter-horror.json index fb9358a69..3b4d2b91e 100644 --- a/examples/starting-first-person-shooter-horror/starting-first-person-shooter-horror.json +++ b/examples/starting-first-person-shooter-horror/starting-first-person-shooter-horror.json @@ -1,7 +1,7 @@ { "firstLayout": "", "gdVersion": { - "build": 272, + "build": 274, "major": 5, "minor": 6, "revision": 0 @@ -343,6 +343,7 @@ "variables": [ { "name": "ControlKind", + "persistentUuid": "f1651ed6-c467-4174-a82a-ab153372b2c5", "type": "string", "value": "Keyboard" } @@ -1265,11 +1266,37 @@ "adaptCollisionMaskAutomatically": false, "assetStoreId": "9c727020616afdd6ba786b8af206a90481f07db0ca175ed6a4cc5b7e01c66d06", "name": "JumpButton", + "persistentUuid": "6c1b75aa-1726-4aa0-a239-c4b9b2afbc9c", "type": "Sprite", "updateIfNotVisible": false, "variables": [], "effects": [], "behaviors": [ + { + "name": "ButtonFSM", + "type": "ButtonStates::ButtonFSM", + "ShouldCheckHovering": true, + "State": "Idle", + "TouchId": 0, + "TouchIsInside": false, + "MouseIsInside": false, + "Index": 0 + }, + { + "name": "ButtonScaleTween", + "type": "ButtonStates::ButtonScaleTween", + "Scale": "Scale", + "ButtonFSM": "ButtonFSM", + "Tween": "Tween", + "IdleScale": 1.2, + "FocusedScale": 1.25, + "FadeInDuration": 0.02, + "FadeOutDuration": 0.05, + "PressedScale": 1.15, + "FadeInEasing": "easeOutQuad", + "FadeOutEasing": "easeInQuad", + "PreviousState": "Idle" + }, { "name": "MultitouchButton", "type": "SpriteMultitouchJoystick::MultitouchButton", @@ -1278,6 +1305,10 @@ "TouchId": 0, "TouchIndex": 2.0247e-320, "IsReleased": false + }, + { + "name": "Tween", + "type": "Tween::TweenBehavior" } ], "animations": [ @@ -1711,6 +1742,7 @@ "adaptCollisionMaskAutomatically": true, "assetStoreId": "523c449a51200d76f5572c755155325f5583aa5a0229949273647f2ac9db8c34", "name": "ShootButton", + "persistentUuid": "e55e35a3-e277-4a9c-897e-454d762acf68", "type": "Sprite", "updateIfNotVisible": false, "variables": [], @@ -1725,6 +1757,25 @@ "TouchIsInside": false, "MouseIsInside": false, "Index": 2.0247e-320 + }, + { + "name": "ButtonScaleTween", + "type": "ButtonStates::ButtonScaleTween", + "Scale": "Scale", + "ButtonFSM": "ButtonFSM", + "Tween": "Tween", + "IdleScale": 1.2, + "FocusedScale": 1.25, + "FadeInDuration": 0.02, + "FadeOutDuration": 0.05, + "PressedScale": 1.15, + "FadeInEasing": "easeOutQuad", + "FadeOutEasing": "easeInQuad", + "PreviousState": "Idle" + }, + { + "name": "Tween", + "type": "Tween::TweenBehavior" } ], "animations": [ @@ -3467,6 +3518,10 @@ "name": "ButtonFSM", "type": "ButtonStates::ButtonFSM" }, + { + "name": "ButtonScaleTween", + "type": "ButtonStates::ButtonScaleTween" + }, { "name": "Effect", "type": "EffectCapability::EffectBehavior" @@ -3530,6 +3585,10 @@ { "name": "Shooter3DMultitouchMapper", "type": "SpriteMultitouchJoystick::Shooter3DMultitouchMapper" + }, + { + "name": "Tween", + "type": "Tween::TweenBehavior" } ] } diff --git a/examples/starting-first-person-shooter/assets/Flat dark joystick border.png b/examples/starting-first-person-shooter/assets/Flat dark joystick border.png index 606bf85b223d68dbdd1be4f5ac851f2018e13828..b4e83e112eb9375cefbb86a6d044a972f8bf4dcf 100644 GIT binary patch literal 3751 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 delta 928 zcmV;R17G~79jgbB8Gi!+006uZmmdHC02@$DR7Ff_aWyqHHa0djH8nOiHZ3hJHa0dm zI5|R^?&H|1(F!EEFDZlRnwi_ z{vfi zj=HzjIAJ?^*aIR1yr3o5f0`Hd%5^<6F6Up zP~Fjl;*KVq#|bI9VKHMl8{r`)iPZ%{-xG$jf4=?q~^F0)IA24NSmi2{R2agHyOAVdeoQ zxPbQ(79!wkIWZ#NI!%xsUi&yP0Snv1t6i5;0fp`1zdpu#*d3k2|9q@mKxK#E_j(Kt z!LJ|`GN7)T^niore1Z;j$NmwypId1iKp=wpMU(OX1$5-Pn-+2DQ3 z3U8HfoorJ0VFQ$enSS6!7WRpkSLewMoFS+@5$ws+?STP?b}+=r0-O&JIbb}yTqpDq z_kD-lXD%rF#snxfL8d2)U`M4yN4i*I^UWcQjl?nfK%C*3a$s;&f%6R|JUAMx!11?+{;EmT?0T5AdBGkQj7xl&{aQ(}c)V~^X*e%3^Jziw;PzI z-Q)Pxrd*^ diff --git a/examples/starting-first-person-shooter/assets/Flat dark joystick thumb.png b/examples/starting-first-person-shooter/assets/Flat dark joystick thumb.png index acd005cc067032996264e510b58c8dbc0c102b62..8acf179f8660be36ccea1198105da35c07154fd7 100644 GIT binary patch literal 2939 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 diff --git a/examples/starting-first-person-shooter/assets/StarterToggle_Keyboard.png b/examples/starting-first-person-shooter/assets/StarterToggle_Keyboard.png index 54eb525bd31884a3c9174fd55987b5d12d415c2b..aabe87250073b2858bdb31fa561d3cd73a5c2456 100644 GIT binary patch literal 3413 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 delta 1267 zcmVk0APj~}1qIg|y;$4-|3eSdR=EXR#Ljv0&8i#El0bNq zCCPtbOc?zjLt;Ha9GISu$7A+MCe}mfMjVETgcv#JMftk%V+^MpZhzV1`Lv#fQTY|# zmVH%Kc?7GE@qgm|MuXXE5Wx|LA|lGXymX>j0mHBaiOCTeWuiGqm?tdkJb~nKEi%ds zbM!h!xMXZ&gS-Jn!ieZO!|zk!l0H6(3xx%l_7e^=V*nBnTyJoR3x(m7Y_~MkS+K|$ z43KnXp6`KzQc6$TjZ&Q@kr{*CB>m&JS3P*(6`NzwM1R5L6lTO&ICHWPon+|X`e7_z zMLC6F0kUhTIljIC`n@PVwP`u5k8%6kln44E_qKxh( zb{b06LXp^Yg%^cvtZ<1NCHc|QMWFQ-3U_9ArK2>SoN9MLNe(uuyLk@p)7%> zJlGHAXO1vp$Jbv&St9IU$JcM8FoX~@Q3#uw_NohXG+4jH|n z0sTU zpp0)pVW8-j`U)3UIhi+}#$BQ8($xh<8D1zSF{H#gGk+;3T}A$ClUFXto{2|3#opr< z1{e9RtJiwXxPn1_o!Y>j4vgs*Rnv1zdY19I z1T&QU(iw}qhn0M_M07d@(=B?wrnEYRR?F(SFg8$}3iw!f8+gbg+55IH7m6mdx_{;G zwJ5GVf)}rJh!K6BOpyHTY)Y&~5puDyja~ZueJI8j_G9+&*0hR%T$yseRbcqgywvf)bNZ*(}g~01Yys?kkB1~sKZ8{!H`|Y zbwYLk4a7gL^N~YT3{4p$1wFQ5i0U&(+omc9F}7{A?VAP;ziABgtA=yGYGnGW2HC&s d-wXbp{R5(SZUFku=BNMw002ovPDHLkV1n@6OJ4v0 diff --git a/examples/starting-first-person-shooter/assets/StarterToggle_Touch.png b/examples/starting-first-person-shooter/assets/StarterToggle_Touch.png index b38bab4da6aa9be1bf4487046f521efd39f403b6..204462798cece3a787bf29aaf3c77d2867382756 100644 GIT binary patch literal 3329 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 diff --git a/examples/starting-first-person-shooter/assets/StartingGround.png b/examples/starting-first-person-shooter/assets/StartingGround.png index 1c66f93412b0f53e8c73213298b347157759734a..edec42fec83b5ace1e1abd51c617a430c9a319be 100644 GIT binary patch literal 2824 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 delta 4495 zcmY+Ic{~%~|G?35%S|dr48@!ssF*Z4Gr2NX5{9`qWXYH#rOT0X4AGpCGvv$?W6Erf zzKF0`XpXX4{MPrM-|zRw^Lnn&^L$>P*Yn5w{d&@5N>rZ7>PPeQa&d9p{6Krh=>)

CVi05h7CM;*%6n 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 literal 378 zcmeAS@N?(olHy`uVBq!ia0vp^4Is?H1SEZ8zRdwrY)RhkE({(F?hNicYo9j)MK}vQ zB8wRqxP?KOkzv*x37{Z*iKnkC`ztmf9$D`E|670}jO?B+jv*Dd-rn8F*=iut7C2Q$ zP`v9mf8wv||V~00!*A{PeEu8xM;`w=bRd-)koZnsVy{m5C+^YZ2 z1Uh(uCNVKMFfeccO=M7DU|<0`QGhJLAMywO=-=p++8q2|e`Wn)>BE9c@^{)RRP6uF v-1ObLsfL*vmO$Mo$MBAy5`hE>kEmfl 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 FV1j1w)3N{n delta 627 zcmV-(0*w8$9DxOp8Gi!+006nq0-pc?05(ueR7Ff_aWyqHHa0dmI5;&mH8L_XHa0dg zGBP$cHa0dk|NsAAUtjX_^4{Lw#l^)&Mn->se~*ukx&LcZ0000AbW%=J0B>IZjxp~R zy4HO*rZ@lq010qNS#tmY3labT3lag+-G2N400G-cL_t(|+JD8F>ypAC4227X5~x;v z|ChDy&bnGlZe(`P@6Ii$Vi%H32h?z{` zA&({os!ck)jd&rN1@*kv&X;~w(7+>zIGK0^u?M`ITYobVd%(4ET6Mt2>z{${OJ<6^ zsnagq<8`6Sd|rA-EH^URbgQvHQ^O|E%x{@$LOs$9eVLZ5Rh; z*XQ!VgKQZc-=f2o!?Ed*2lmdfbz>*?te|XMb>FPA<&m<}EJWI#E9%{YoXZKS(#-Mi zEz^go>*bgJ1}U?hv%<;dkt!VPW>+x%4v0{XX>l^EM0ovUQ4t~&+a0mURTHt&FNy#F N002ovPDHLkV1nr&BUJzZ diff --git a/examples/starting-first-person-shooter/starting-first-person-shooter.json b/examples/starting-first-person-shooter/starting-first-person-shooter.json index cbc9d414f..0d4e90e1c 100644 --- a/examples/starting-first-person-shooter/starting-first-person-shooter.json +++ b/examples/starting-first-person-shooter/starting-first-person-shooter.json @@ -1,7 +1,7 @@ { "firstLayout": "", "gdVersion": { - "build": 272, + "build": 274, "major": 5, "minor": 6, "revision": 0 @@ -298,7 +298,7 @@ "gridColor": 10401023, "gridAlpha": 0.8, "snap": false, - "zoomFactor": 0.36732575450590194, + "zoomFactor": 0.5468750000000299, "windowMask": false, "selectedLayer": "", "gameEditorMode": "embedded-game" @@ -325,6 +325,7 @@ "variables": [ { "name": "ControlKind", + "persistentUuid": "f7dcc002-63ed-45a9-acf0-51088ff88d26", "type": "string", "value": "Keyboard" } @@ -1073,6 +1074,31 @@ "variables": [], "effects": [], "behaviors": [ + { + "name": "ButtonFSM", + "type": "ButtonStates::ButtonFSM", + "ShouldCheckHovering": true, + "State": "Idle", + "TouchId": 0, + "TouchIsInside": false, + "MouseIsInside": false, + "Index": 0 + }, + { + "name": "ButtonScaleTween", + "type": "ButtonStates::ButtonScaleTween", + "Scale": "Scale", + "ButtonFSM": "ButtonFSM", + "Tween": "Tween", + "IdleScale": 1.2, + "FocusedScale": 1.25, + "FadeInDuration": 0.02, + "FadeOutDuration": 0.05, + "PressedScale": 1.15, + "FadeInEasing": "easeOutQuad", + "FadeOutEasing": "easeInQuad", + "PreviousState": "Idle" + }, { "name": "MultitouchButton", "type": "SpriteMultitouchJoystick::MultitouchButton", @@ -1081,6 +1107,10 @@ "TouchId": 0, "TouchIndex": 2.0247e-320, "IsReleased": false + }, + { + "name": "Tween", + "type": "Tween::TweenBehavior" } ], "animations": [ @@ -1407,6 +1437,25 @@ "TouchIsInside": false, "MouseIsInside": false, "Index": 2.0247e-320 + }, + { + "name": "ButtonScaleTween", + "type": "ButtonStates::ButtonScaleTween", + "Scale": "Scale", + "ButtonFSM": "ButtonFSM", + "Tween": "Tween", + "IdleScale": 1.2, + "FocusedScale": 1.25, + "FadeInDuration": 0.02, + "FadeOutDuration": 0.05, + "PressedScale": 1.15, + "FadeInEasing": "easeOutQuad", + "FadeOutEasing": "easeInQuad", + "PreviousState": "Idle" + }, + { + "name": "Tween", + "type": "Tween::TweenBehavior" } ], "animations": [ @@ -2527,6 +2576,10 @@ "name": "ButtonFSM", "type": "ButtonStates::ButtonFSM" }, + { + "name": "ButtonScaleTween", + "type": "ButtonStates::ButtonScaleTween" + }, { "name": "Effect", "type": "EffectCapability::EffectBehavior" @@ -2582,6 +2635,10 @@ { "name": "Shooter3DMultitouchMapper", "type": "SpriteMultitouchJoystick::Shooter3DMultitouchMapper" + }, + { + "name": "Tween", + "type": "Tween::TweenBehavior" } ] } diff --git a/examples/starting-first-person-survival-crafting/assets/Flat dark joystick border.png b/examples/starting-first-person-survival-crafting/assets/Flat dark joystick border.png index 606bf85b223d68dbdd1be4f5ac851f2018e13828..b4e83e112eb9375cefbb86a6d044a972f8bf4dcf 100644 GIT binary patch literal 3751 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 delta 928 zcmV;R17G~79jgbB8Gi!+006uZmmdHC02@$DR7Ff_aWyqHHa0djH8nOiHZ3hJHa0dm zI5|R^?&H|1(F!EEFDZlRnwi_ z{vfi zj=HzjIAJ?^*aIR1yr3o5f0`Hd%5^<6F6Up zP~Fjl;*KVq#|bI9VKHMl8{r`)iPZ%{-xG$jf4=?q~^F0)IA24NSmi2{R2agHyOAVdeoQ zxPbQ(79!wkIWZ#NI!%xsUi&yP0Snv1t6i5;0fp`1zdpu#*d3k2|9q@mKxK#E_j(Kt z!LJ|`GN7)T^niore1Z;j$NmwypId1iKp=wpMU(OX1$5-Pn-+2DQ3 z3U8HfoorJ0VFQ$enSS6!7WRpkSLewMoFS+@5$ws+?STP?b}+=r0-O&JIbb}yTqpDq z_kD-lXD%rF#snxfL8d2)U`M4yN4i*I^UWcQjl?nfK%C*3a$s;&f%6R|JUAMx!11?+{;EmT?0T5AdBGkQj7xl&{aQ(}c)V~^X*e%3^Jziw;PzI z-Q)Pxrd*^ diff --git a/examples/starting-first-person-survival-crafting/assets/Flat dark joystick thumb.png b/examples/starting-first-person-survival-crafting/assets/Flat dark joystick thumb.png index acd005cc067032996264e510b58c8dbc0c102b62..8acf179f8660be36ccea1198105da35c07154fd7 100644 GIT binary patch literal 2939 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 delta 1267 zcmVk0APj~}1qIg|y;$4-|3eSdR=EXR#Ljv0&8i#El0bNq zCCPtbOc?zjLt;Ha9GISu$7A+MCe}mfMjVETgcv#JMftk%V+^MpZhzV1`Lv#fQTY|# zmVH%Kc?7GE@qgm|MuXXE5Wx|LA|lGXymX>j0mHBaiOCTeWuiGqm?tdkJb~nKEi%ds zbM!h!xMXZ&gS-Jn!ieZO!|zk!l0H6(3xx%l_7e^=V*nBnTyJoR3x(m7Y_~MkS+K|$ z43KnXp6`KzQc6$TjZ&Q@kr{*CB>m&JS3P*(6`NzwM1R5L6lTO&ICHWPon+|X`e7_z zMLC6F0kUhTIljIC`n@PVwP`u5k8%6kln44E_qKxh( zb{b06LXp^Yg%^cvtZ<1NCHc|QMWFQ-3U_9ArK2>SoN9MLNe(uuyLk@p)7%> zJlGHAXO1vp$Jbv&St9IU$JcM8FoX~@Q3#uw_NohXG+4jH|n z0sTU zpp0)pVW8-j`U)3UIhi+}#$BQ8($xh<8D1zSF{H#gGk+;3T}A$ClUFXto{2|3#opr< z1{e9RtJiwXxPn1_o!Y>j4vgs*Rnv1zdY19I z1T&QU(iw}qhn0M_M07d@(=B?wrnEYRR?F(SFg8$}3iw!f8+gbg+55IH7m6mdx_{;G zwJ5GVf)}rJh!K6BOpyHTY)Y&~5puDyja~ZueJI8j_G9+&*0hR%T$yseRbcqgywvf)bNZ*(}g~01Yys?kkB1~sKZ8{!H`|Y zbwYLk4a7gL^N~YT3{4p$1wFQ5i0U&(+omc9F}7{A?VAP;ziABgtA=yGYGnGW2HC&s d-wXbp{R5(SZUFku=BNMw002ovPDHLkV1n@6OJ4v0 diff --git a/examples/starting-first-person-survival-crafting/assets/StarterToggle_Touch.png b/examples/starting-first-person-survival-crafting/assets/StarterToggle_Touch.png index b38bab4da6aa9be1bf4487046f521efd39f403b6..204462798cece3a787bf29aaf3c77d2867382756 100644 GIT binary patch literal 3329 zcmV+c4gT_pP)Mx*Y;j!XU|U>?Oe|6YNhj^?{dC##zPrsY-|j9z25B} zkFOz-h_5G&#szpho~Zcf5qRWvbrbTg{G$FpAjW~Ep|hs(YO|m5X|^*rV@Nvdie0+g>;Q3S(Hd*^&f;t-mq(2U0qF@{P6hk z%p5r?L1@nyZN;*RNmyiQSbHp+q7P5s!G^R?jV5SWT_3x6tEsZG zk~I0@jorIVvCOGhNa1CFB29jPP@3;dCBD|@+NMc#rT_a!zQW!P?HRT&`0utZr%|ClQTC>DH~=6b{!@D5Nvi`2>PkS?{M+^%X)S z_M-UgNu2I|GER4Hk5QM{N}14Cnu+>SPOZ1LF@=&yB#-TVvnEH69;d;7Nz^Wk27#n?*wvFHJWHCfC zGBP5!bk+hQ;dYwb0(a`Q)}XW`%*b$p`X0ok#lSwq0N53KKIw|_@ut=Qy|}J|PMsK$ zmd0)P$pOHvI&|o;5J602*w)BRQy5?oUK5?1;|>U-vtyL52nzv$q~prviTl|KK`=jU ztE7LNe@cHpA0s#62%+4+e_x6OeDJ})sH&=p8XCf+$uAp32Y>n1Ip#4sw<-{bhXZ(s z06<8vH2q>@QDJ*YSQC67?&0rrlUN)fl;z8pQ%%h?G%#TJh-c2cCtR>9s;#Xx+tk)R zN4>rGsY;yJ%${$on`3rp2;j%$&Sn%KRh84#20Y07-X@~$>D0L@d|{2*ktK4zZ8VA?=uAlv9!HeDZldv zKMK4%saT0&W+8mD8QlMsuYg(@{1V^J6u z%PrBN!3Wv4Z>B-kfJ%hgjHR z@bBO^I0=Aqm{O*w7KO`FR1ib#+O^X>hHx%koVr6pBwIFrNA9yVno3--EXatdX=pbC z_X!vsIG%jc8yy%HfJfh98PvD}?Kz%-7~qP%B--F!xLjvXkI3@~{yD9T&8P(%H7yN( zuTUyH== zVO*sYDXLKHf_9ZcrEr?`~^PqyWN7L|IOXIcc~DdG^QNfuj#;X4BRujaS)C}R6zk29e)ms>+G@(4^zqF zSfTXy4`e-{2)Is+8MMisW#Z$HyUER{LA2G9s89ds6YkWBN!P4wP{w2!#4e~CJ9|22 z{%-sJq2$<#P7mjtSZsRpPLSb3xe7q03_<#Iux8)f2B&EO1`a{=NorXN;Q1#kkUJUb zw0O+KN>}nbCVo3^J4a=r6ee@=r4w+hP^Q~)F@SjTre*|kM)B)_Q631COj5by*cMxQ0dH z;zc~wVF6e?=T1+!qp&LJEC*W^d=D(fY+??l7En4?D5%%Ure@2Iwzi$5nT)l}1f3Gc z$WkF@;K^1H62xK+$YE8|i3Rsz6n?gWIhW;Ua`%dXV}*iAS9iM$GmZiLR>iSGc_f6AtFroRfb}W| zp`i1fIx%dPzaxNFXC|`i#B)mkTsEEw$rdS}IMJ%;V7?6DYG7cUnTUZwV=~sn(;UJ1 zqGaW9p`P?=7S0LiV7^RIiyc`kILE81VB^dc;8Aro&dDcEvntx6lricoMRv8|lCd~o zdCs%mYY@4LeWSN$YX#J%M@7`+E$AvnR2r#I+)j_AeSNPh^MUArIyjFz^WADiSLAQ z6>U+Hg!G!mwxkmYC?~7pSfK<0gu6UPdrW>F0llBz0gQD#CRtx7TH zt2kCDa#8LAxPWM=7aVuNPiup%q_SVZIP`x*%}JJ5+|)2=$$h0I>@Kd@MGBX~MIOWB7fLP7?@Ivem;#>0*lx4pMJVuUw=X3v-XeVp0r& zU{J2CQd{?&6pALdSgVYUMeqDlIMd0Hu>dnIu;&D9g)%fWM5Tf(bMF#_peGi_} zmE;IL&$mzKs=;#b+aY)?bai#n%9SfgqnQc30;LKUH5NOQ zW@RXL&vGWwV{K!~JdsG;R4-h+ew{R$T*B|BcUl4EqyL>tIksv6m_qS-J%3d%T)1#S zS{04P%|t)HN{u^6-lFqDFdB^;8F+G*?mcX`oJ}0L zapOjhwQWjHud%VQ&gb+0N9=~w^VQYW)YQ~O^C~MzlOOOv;v|aCH;-!OZz4^87>^Ip zHo z<8qRgq&1yLK*r(JNF=gdG})^Kqj4h;h)81nd2DR#fY3W$JZcuhh>BiZck%| z215bQVSi*p(dsqGsng0XlcP6^qcIK!$|x{; zkj`ncH_8k%(0>w&lVpVM8NWY@g|R7M%%c!13GGjE3d%HDVHPJQMj20$1V!9qe>(k8@C`0e zfK|jHiXs|wufS2sC^Wk{N^#Qcni&FJoIRVAJa)T_& z#E(d`7p3ME8Gj=zs>&l7EXcym(wWK1(i_Fqk3%G{UMq<9viM*lD!R^>2eLzXx&7rl|9b z**0(+%KT?n`P0JlHbu@nszZTIQIP&hQ53g1QRu+Pym^$f^wxjpPYISZFgH#L()CIU zl2jC~Tm1ak(#sgFm>94|!IKy*Ht~Fj& zQHv7n;%r~6Gm0!VB`RA*_*q#C!{A`ETh@!RY84%B;K8>jX6w#>GfK8HUf(P0242!u z-P4PNd36~H+KkMHM{@*`+`~gCa$gv0fA^iXV002ovPDHLkV1l+s{uuxO diff --git a/examples/starting-first-person-survival-crafting/assets/StartingGround.png b/examples/starting-first-person-survival-crafting/assets/StartingGround.png index b38c546fe2ec26ab04ba663a09255bbe4d3c9a6e..edec42fec83b5ace1e1abd51c617a430c9a319be 100644 GIT binary patch literal 2824 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 literal 648 zcmeAS@N?(olHy`uVBq!ia0vp^mw>p0gBeI3XPVOtq<8{+LR@Da@@?FtF=3BG`!?gR z2VY$TigOltL>4nJ=qZCRW5rVYG6n`FB~KT}kcwMxXWdL{HsE1t{qz5S%$zR<;U5(_ zGtz&{`1fp|vPdvg^#6)u1x6l89y3)LRYW;&@hi3nIC1Q(RGPm^d0y$(6Y&gZS1+HF z`Oov@#IKxH#Wjw`Pt|9B&AHI^_;?qK?`?|(dTf>p^w?)E-nR5J<4eO6+n#3!6tPU@ zR`>lW_PXHjwAhNf`KK`Wo1`( z+V2i?O6M+?E!B_0Z0+;5>#oR>?4I}fW#1bI({|f?ox2;41r{#o(wp&R&%yv_h1?q# z1I|nDe=4xzZAZ0rVSIr8E1`g|FTZ`+v-a-a1@G=3yr#V{*7_Cm*L#|`?S8QY+-`sD zFgG?s@9l%fzNQ}RVk#;+s;Rs-g{>?;iYx={dIk_ z-oB7Y`I+{zV(0RvK5-tVm6_svU2_tpC+unnx%b%Yv21SDJo5*i^(vUXHqF^C>Bk)2{cb?#wBPsXv;Z&ED~Aw$)NAVTP_vKM#j!<)7q>*8F_g}a_-yc4o&-r|Q{G8AEWO;d@RFsY=0RW)l>f-Dp z)2e@=s37AUex#{P_TyYG#sPq``o91HGDTY!f^j}5IKbfPev%ovNJn=^0C<+Kyb~rb z^9SM)f$_fBh{S4HJrWaE@vI~q0OXmj&W?Tw;IiNx);B0(g6AiNsb-6=W%w>ANTIQOi4J`kWjl#f`<& zxV3w%ONnP)3#}_K9CUu7Qa#Kv7|)31{_<3(9({vkfZ1Je%4LW67(3`q)u^r-b~zcX z!e0hOAnAux9#?iIoXn5d1-u`4}|&a){!P&Ut6O-Fj%=@XRD59On!$KLlI{6O)a7BmtkvvnKr3K zbPpg8&JW$RJ@!Ji874s#GhgO(bF@A{8gfZ`#+3a$qojo(2!7pm?fRMq`;1ZsGNgUSBBda%R&dXIlj0shQf(ByS=WKQ zQM8?T*>CK=94~26554gYWH3y026Av%Yqq$b(Iz)9l%UbU=2cqqoA+$%5dK|#eXU^o zHH|1|ft!3;!*_@LH_0c_#t!eL@YI464lW0e*C?v_$SE5AkbVe+X9GVX{*V82Cw?^G z>F5)%{XR7|4o&kezEKIbFG%Q}BQ4RXJo3df1qB6_h2DVy+|+RSm?vQ8BZYD5mtsa=Z4S< zNhY+mMClY~d9oHtAe9r=Hl}|MH*})hg22k!ra!hZ-&z#Y&(BrZld{zGc&yDu&Nq4_ z^iF9sB4J~uPy|hxm0gTi$go+P{S|H!hUfO!)7^8M<%}I(zoI;g9Wv~jia}dY5w~)L86#0cov&-`xs{wHGk56}U$VIu nQ~1a;%9N?jB0T~Dx9xqkALDSP(B~A{y#uZY4`&8E^tXQimD|0$ literal 6328 zcmeHKXIN89w+_8XQ4x@)F>nNh^d1O`)JPRjK&7PYkO(Ox0YXuV1r-EDJ)$%#AR-_j zD6vo!M2-gpd%*@G3JPKa+qZ-LyXTMh-t*5q&(7>Uv(~%Tdgqq}PECDSb(%=bedwKHW;y6ZiMXv%r{(6qBXIXb(kGn>wb@>#=hIuW7opz1 z?b8dt?Nqb+!o;!G%Db=H-&xy*-WZfNp~&0$2_7eJ*$y4AeADz;CWbzaaJon>{PG{7+;`pr%J%D7L&m1(VO?VFJGD9*+aK4wP>FCIcON(&OsF3*kqNV z@}XuS9>r+WDYHrTzSh~a(l$fNDzs3&*qf&X^#7c9^6odzv?f3_{ezr$@RtXy33+^ zGv6nt-F1T#h7JuV9MUGf&z`oSd!zZ5#HzZUiKp=?cv($r`k|mDx>v)NQlseu64*8@ zs_n6!5d6oJ9U6!r~Oz% z&!|(S`uh1`@g)O>_*9*bW6Q}69)6sbAftj{unl0=#?0ZhZ|ddIvCg2nHh44RN(w^SL>isc_peORIek?xMZXth&Ai|EdNB` zhmr_blA2&bYXI9Tzjb~1vhBgRFRgQFVtG^pe(Rb2Q4^Y1U0v%9J9px|vg)j#54qTP zlkVIo54*PBpg2LB&;KhTDVexhVc==zn)|`a7p+?o{A#xQKcr+88L{Jb7zQKpi~;KfDUXHB!~*2zM7JBK+Q z|E-FvNS&cQ$|@Cc8}ez#bSkfuuT`Oi=dX^cF13Hd7gnQ4K~kQndoU{iO=1OL+u5e9 zTMIq&@3#1F4lMY@`Fn>}J}du>-houdO)Y5hrB7=Jd40VvMz4T-Kk$#6K36DV$~S%L zVh5dMl)7w8v#8O^D=w36D;>M{iTOwC^yM7uf#?z&mFKB65!AoMA_L)+S5HlEo%>m7=Z;?N;``i18iG&AoVJGqv)uBV_HyMcKKB zEW6W>_-OiJaW5;`+oF)!K%XX<>PmzcjM+zR++nf)ZN9X5&y?v4 zM8PrRTf~>>Dd8J3JXRHwoNNqi1D}O0!}vcidQi1*)pjH+co|6$waglSwZSrpj;cIz z;DncWMwHE*6bq;GrF%0pZJan)+|XP1eaKBqIy-9F{>Qe%S}vI?{(BdR-rq|ak*%mf zQTykrFc-c_=Otw*P-P2i__yx1)!ngEc;DH$0 zY1=rm4PlgHKF-x#uQ;;COb85oqyqQV03o+wYDBf#Oh#ft#HcrSlud<2usf-SJuw3AXm03INrL#4b(zL+AV z!X|Mk;Jv&X34>0WNFu1PKu;g2qd){eaR?j&1$UNmV=yp#O{krS#isZ=xqN{DpQtd7 zL?Wahk+HF{h*&H_APPsK$z(DTg+XF4aL@uSj^j({QaE31E{FJx;RJ}8B2dm;0Us*I zq%#E35-JP^jzhoZ#}j&beuL+Wzpw!Eft1pPNHhY4?F(Am7g#OV(><=EE zkiLLe5G`T?&M^RAV*Vopi}}r87%hsNEQiHJ0+9d@G!=tU(LaTBrFr^%^N>>z&gBUw zy+E>mvXpSy-^uzZH~Gk9IX@->x_`s{$@**Ulg6Nxrzgcpz>JosM{}aW>5`oBHAc&;GVshyGZ~$E4UnKRlpZgEf zg<=vJBqA9LC(zj}I1Y=$!Wj$}363R_S$J^5NH~BnS;jYXv4Aa!rHcTEaIjLa8c;%$ z)j+MjkU8&X`&bSjX9|VE!%<{78s(2BP%vZ)h5$o;DHbVT(_gE#L;epw>?RF<*aSeo z&wb$L0`67FZ=2N@zT_1Ci@z`P@GnLHp??SYC4PU?^_#9=V&IpIf2->^UBAS@FB$(< z*Z&(`n%_Tn06usEiUmJrEU6oJf*(Sv40l&2$S3(X5!Nzs3~V%nZb4!Q1Ys_}6r=|i z#DJY@5}K#8+MtH!bX#MGg-ZLuE+dI^pu|zYlb@O)lSes#1&!rOI8gafE+9q65CTzB zr8znHOK%QeKgGMHKC|uq84vx8dyqe;45*Gk8Z@*5E~^K$>Pb~9?`hXKocwU7^U1`;Iz6=& z%`%#(pzo|*+=euZ`kIE*gZlSs%jVLaMcTfu(ywGkQ$O*Nd5HoZ_4RRWWRVML>g-S9 z>s-BcZg0v`B5_x^=gja9A$>%f==4>Njg2+E)1CeCPGn;Q_I_i_uy@u7h>ezB%G>YCFc`T%I?Cl^rt7sP1 z-mJu?qrx4L**$uyl50FV+Ga4@kh{D1X)P^0xud@ucY~G{GuO4C{%)uBHkRe;n}b?i zQ`9=+<|f7@S9vQsC96+W-UxB{)n*!0eX2q#=`JbQZ!yNR%y2OWX`$ZS6{{LJ!@0GD+0&Pm;oUZG^S*%OHwA}l zZN#si*t8+DsCw0}c0GUFAN6-g!9kk@YQr7f9MVs@-5F?yPC}_`1Je zrh92Pr9+aqJ0+MeThRKZNA*43q}Sp^o%XnHUGH#tgELn>~)y{pLA;vnMUn0BaF zK-+>t?5wRI$GIa~LOZxrjiXRS`Bb;!0SM0v#nEKNbw3DuFKT*v91fH&bF6*2UngIGpYBc}= literal 378 zcmeAS@N?(olHy`uVBq!ia0vp^4Is?H1SEZ8zRdwrY)RhkE({(F?hNicYo9j)MK}vQ zB8wRqxP?KOkzv*x37{Z*iKnkC`ztmf9$D`E|670}jO?B+jv*Dd-rn8F*=iut7C2Q$ zP`v9mf8wv||V~00!*A{PeEu8xM;`w=bRd-)koZnsVy{m5C+^YZ2 z1Uh(uCNVKMFfeccO=M7DU|<0`QGhJLAMywO=-=p++8q2|e`Wn)>BE9c@^{)RRP6uF v-1ObLsfL*vmO$Mo$MBAy5`hE>kEmfl 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 FV1j1w)3N{n delta 627 zcmV-(0*w8$9DxOp8Gi!+006nq0-pc?05(ueR7Ff_aWyqHHa0dmI5;&mH8L_XHa0dg zGBP$cHa0dk|NsAAUtjX_^4{Lw#l^)&Mn->se~*ukx&LcZ0000AbW%=J0B>IZjxp~R zy4HO*rZ@lq010qNS#tmY3labT3lag+-G2N400G-cL_t(|+JD8F>ypAC4227X5~x;v z|ChDy&bnGlZe(`P@6Ii$Vi%H32h?z{` zA&({os!ck)jd&rN1@*kv&X;~w(7+>zIGK0^u?M`ITYobVd%(4ET6Mt2>z{${OJ<6^ zsnagq<8`6Sd|rA-EH^URbgQvHQ^O|E%x{@$LOs$9eVLZ5Rh; z*XQ!VgKQZc-=f2o!?Ed*2lmdfbz>*?te|XMb>FPA<&m<}EJWI#E9%{YoXZKS(#-Mi zEz^go>*bgJ1}U?hv%<;dkt!VPW>+x%4v0{XX>l^EM0ovUQ4t~&+a0mURTHt&FNy#F N002ovPDHLkV1nr&BUJzZ diff --git a/examples/starting-first-person-survival-crafting/starting-first-person-survival-crafting.json b/examples/starting-first-person-survival-crafting/starting-first-person-survival-crafting.json index b4c3ab188..953cd45b3 100644 --- a/examples/starting-first-person-survival-crafting/starting-first-person-survival-crafting.json +++ b/examples/starting-first-person-survival-crafting/starting-first-person-survival-crafting.json @@ -1,7 +1,7 @@ { "firstLayout": "", "gdVersion": { - "build": 272, + "build": 274, "major": 5, "minor": 6, "revision": 0 @@ -1173,6 +1173,31 @@ "variables": [], "effects": [], "behaviors": [ + { + "name": "ButtonFSM", + "type": "ButtonStates::ButtonFSM", + "ShouldCheckHovering": true, + "State": "Idle", + "TouchId": 0, + "TouchIsInside": false, + "MouseIsInside": false, + "Index": 0 + }, + { + "name": "ButtonScaleTween", + "type": "ButtonStates::ButtonScaleTween", + "Scale": "Scale", + "ButtonFSM": "ButtonFSM", + "Tween": "Tween", + "IdleScale": 1.2, + "FocusedScale": 1.25, + "FadeInDuration": 0.02, + "FadeOutDuration": 0.05, + "PressedScale": 1.15, + "FadeInEasing": "easeOutQuad", + "FadeOutEasing": "easeInQuad", + "PreviousState": "Idle" + }, { "name": "MultitouchButton", "type": "SpriteMultitouchJoystick::MultitouchButton", @@ -1181,6 +1206,10 @@ "TouchId": 0, "TouchIndex": 2.0247e-320, "IsReleased": false + }, + { + "name": "Tween", + "type": "Tween::TweenBehavior" } ], "animations": [ @@ -1925,6 +1954,25 @@ "TouchIsInside": false, "MouseIsInside": false, "Index": 2.0247e-320 + }, + { + "name": "ButtonScaleTween", + "type": "ButtonStates::ButtonScaleTween", + "Scale": "Scale", + "ButtonFSM": "ButtonFSM", + "Tween": "Tween", + "IdleScale": 1.2, + "FocusedScale": 1.25, + "FadeInDuration": 0.02, + "FadeOutDuration": 0.05, + "PressedScale": 1.15, + "FadeInEasing": "easeOutQuad", + "FadeOutEasing": "easeInQuad", + "PreviousState": "Idle" + }, + { + "name": "Tween", + "type": "Tween::TweenBehavior" } ], "animations": [ @@ -4551,6 +4599,10 @@ "name": "ButtonFSM", "type": "ButtonStates::ButtonFSM" }, + { + "name": "ButtonScaleTween", + "type": "ButtonStates::ButtonScaleTween" + }, { "name": "Effect", "type": "EffectCapability::EffectBehavior" @@ -4615,6 +4667,10 @@ { "name": "Text", "type": "TextContainerCapability::TextContainerBehavior" + }, + { + "name": "Tween", + "type": "Tween::TweenBehavior" } ] } diff --git a/examples/starting-first-person/assets/Flat dark joystick border.png b/examples/starting-first-person/assets/Flat dark joystick border.png index 606bf85b223d68dbdd1be4f5ac851f2018e13828..b4e83e112eb9375cefbb86a6d044a972f8bf4dcf 100644 GIT binary patch literal 3751 zcmV;Y4p{MtP)005u}1^@s6i_d2*00006VoOIv0RI60 z0RN!9r;`8x010qNS#tmYE+YT{E+YYWr9XB6000McNliru>IW486)D;&{k8xA02y>e zSad^gZEa<4bO1wgWnpw>WFU8GbZ8()Nlj2!fese{01gjHL_t(|+U=cxXq@+Xz(3D> zC+SX79NUp>$yVSJ$>iiJ))lIUFb5nqkA@bSF^@SUfk8d!SOYF+9(=fJWpSxPJu{Ww|g-LpUR+CJNoe0RD#-Fx?Z zz*x5LUcdLeAD-v?eDC{yUoebKL@WgNh(uz+rWQAX1fn@1BG3aG0qFpq0NQ~LAn5&^ z&m55PexCtuflh)EfITSm(lNuxSW*0eB9GRYA65q#Of&1Dq9i`D~*IS=R#gJ@af2 zj`KC(dEj6T3HK;6z=yyua;ooNPMlAhfPRrKIz9et%)c z1gyd4AsE(!z+VD~e3a=*$&6<}hm+&y&zOL1TFX~8+}1|tj&1;^r>?WOFkkB#@jqOZzpk}x=+so!i|#vw z1TNINd`->Gbad{#`{yS*chc0{TV(oQ32JDOOiy-oAe7JDH zah!rByuFkmm&@Tpl8C;TyjZ>UhgB1>w|{Uz;55*KQOisyyp$Rr9jluX+#4Mn5;$k_ z`GOvSbG^~(%okQ%z^D5M4~lS_hwmaYYL|8qPCwm0c+l4+`04(^gASa+RZjp(-z>5~F8*BcRuCoU^i9H_Rz~wGxd<*~fr=(R~rG+co*> zX-o*G`}XdO)N}^ivp3eRsvpyMHwZNfNmTwInTVHdrFGddU?ds~A~t>^m~ zn{H_|GKhY8qw5JbRDvVXSO}4mCSOAmLgiE>8VhZjfZj6t0lO9rOVo3q`X#uxe{e|P zf9x6m?aypfs3UgP&wha;CZDmv zj`(QFC0O8F7DHo$yuRMIBhK38ci%y5!GW=H`n>1(6`27=Ubom8W#VX2O-OJ5vmJtS z#kyakOt`3YuGWmKo&m-AhRJ7?ksvDzSqdw`qI}b0W|Wz)7gU11ZFE1icp2qJbGau| zpIn$&KLd&!vQQb7Kpf|5g_Phwwon-r;)Qif@SZ)fb`9#ZH6TW%x?yo;+oenKxm7cu z26V{eGb-5)Yfy*QF2TZ(#mJ~;8GdjK_`Jo(s4&m1ItKK{e>d^Tv)hdN7zf_XfEvv0 zH~EZei~|YifmKSd9I)6J)j);S1Z2O3#;6hls|omw#l@%+U%IaeiNs>T;F2!d7|^Iz zBjPSfIGv8W4nR{T+h_6_)oet9ExE|u60G9bvptOJA`;CX14b+&Ms@uok}se=NZP0> z3cK?JR1?ssHc#aVD7NjIQEfW%OYnwwKiIN$8#}so<2a6mwoY*zht95DJicvvIS%&( z+?#rNqV&O}u)T99ZEY)$2X1Z&ky%`@t5t_Iw}c3XTM36-2?m?^eC7tKTI%82^93yF z;nGeZKsHwS`?p;rsd<{%XC`9+JCuwSK zF6HgPd;yEffZ^6Qc6Rp=2-=q?jZPpKq-$3X;kKfSXnVeZ1rus*rE_PuH5*1=AK=ir zql-|uwfJFN(eF?TxUrUqR9?W$;sP@_uA`c>h#LUpayh1_uCutXP_lq?#k^@@exAvz z6J%Z2qTk?TU6-kA6D%&w7yH)x?g=T{q~+z?T)%dOrA)@ILW8ojl;QfdD=aVHF6Hf+ zd;w=lO1;@EHzuz#Klg=QjRs?3evTX0ua-IocsB#ynMau~^(d;!XVX)-?lLVcA#N{O zMyywEFJ;IqF0zza;MVNTjUH|$U%;7-9_`l6&+Q5|K(ldU@@lybcPp<1>x_lhs4|mz z0!rHAVN{tbcLh|Kuy`0%=IXr+sA?I|s4^?gIRK85uz(m<)lW&@6>vH1q$y!C+AvDQ z&1Rf?0;c0}7c2`JRU?s3$KAXx7=YhdT#PF5$(W|=QDMf`d`<|$Z!JDXh51eXAMO`0#|IW4qr#kB zQ$WeeSsMo$6(l3BtSz8dn}20-GAhJ}`QyKbOYjO-hK)+`iwFN=6#;WUw>TMPYV_>nTXE< zzqGg+W#UX~JU&x+23$#*CoFD8nHXN{AJ!Dm4apf;SvGAL8z7w=A3amFfZi(UVT+%! zLEc&KAJ^ZWP2~hIYcVuRWY%kFOB2v*Ml1^&C86+R&!G}CARsDl<87s77)6jl<@lps z^hg0OCE}Bo1&tE2-*9Thos9|TWx?Z?1&wlNzk&tyvfy_uu0}btU%>);9r4@N5gP?C zDJpLj`r<&LFZ_IFGPAw2>lSdxVr;C#-y{>G9~S!Jq8`)Ww#W(W26kg@;_f5n{Wc_E zIv#gbdClT%tU5|l-b^RrnTiPrkV=e>06(;F8xJt-xj{t*^cs?HW4o#vdBxFHIabOW zOA;`dh|em#X>m6I6#k;v(pz*F{2(`HrhdP@v#T4}ZnB=M#!e%{1V+@3mASzTxT@D96d% zoS8bmy|b$m_=;VchTvaQg4 z1#_zM)l?#0_Jt@;CC)Z=@%)&=%h>cxJ&;l5~~2K|5#ML)^+@9v`f+2v$>q1ykkZ04u#>mPg-c-Jo- za>tG?THDs|qvq$n;Iru|-*mbPucgLE|8uiWKsGx9y?ggXMdc(gQ0rGqMA*^2o0c`k zf=ihUHzu#5s(z3!p(=;Vv82jZCHO&ZPEXBj>FEBEBXr_}<^78b^R%@+hU45nho5y_ zrY5hV{%AgiU6sS17Px&zU1z}ivf;{n;g5hwEnmH+re=2T>bWbSYL3aP6D%*^u66My zRCzO%7=6EH#@4kIotraL=`9`I!?m6bbGayN!>PpR`I;Km z)M~VPqk{)T_yG{B)pHEDwxSA|#f4fHZ%mcfO1Wvgp#=npL}Q_#=n>!_fG&(;m<9gM zU6d2)bi8ICIwXFGq_@)gE#P%*QNyFi0N+LB?P^ad*R6ne*b|L)spxl1#MQ-97)IrI zss_5-x)g8~5&s<6YNeK8Re9S_;`v&@{L-wVM+Ck_jZfWFfF#N=$O%7`+PW0*ei;W3 zBD?|oQ5~0O7x*R02{$8WYPq>+I05g_+aK#d^&2956F5*OA8Q2o87lAjIomaafDe@D z2%?98KLMWeQD$_3UjZMGlb37exR7 delta 928 zcmV;R17G~79jgbB8Gi!+006uZmmdHC02@$DR7Ff_aWyqHHa0djH8nOiHZ3hJHa0dm zI5|R^?&H|1(F!EEFDZlRnwi_ z{vfi zj=HzjIAJ?^*aIR1yr3o5f0`Hd%5^<6F6Up zP~Fjl;*KVq#|bI9VKHMl8{r`)iPZ%{-xG$jf4=?q~^F0)IA24NSmi2{R2agHyOAVdeoQ zxPbQ(79!wkIWZ#NI!%xsUi&yP0Snv1t6i5;0fp`1zdpu#*d3k2|9q@mKxK#E_j(Kt z!LJ|`GN7)T^niore1Z;j$NmwypId1iKp=wpMU(OX1$5-Pn-+2DQ3 z3U8HfoorJ0VFQ$enSS6!7WRpkSLewMoFS+@5$ws+?STP?b}+=r0-O&JIbb}yTqpDq z_kD-lXD%rF#snxfL8d2)U`M4yN4i*I^UWcQjl?nfK%C*3a$s;&f%6R|JUAMx!11?+{;EmT?0T5AdBGkQj7xl&{aQ(}c)V~^X*e%3^Jziw;PzI z-Q)Pxrd*^ diff --git a/examples/starting-first-person/assets/Flat dark joystick thumb.png b/examples/starting-first-person/assets/Flat dark joystick thumb.png index acd005cc067032996264e510b58c8dbc0c102b62..8acf179f8660be36ccea1198105da35c07154fd7 100644 GIT binary patch literal 2939 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 diff --git a/examples/starting-first-person/assets/StarterToggle_Keyboard.png b/examples/starting-first-person/assets/StarterToggle_Keyboard.png index 54eb525bd31884a3c9174fd55987b5d12d415c2b..aabe87250073b2858bdb31fa561d3cd73a5c2456 100644 GIT binary patch literal 3413 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 delta 1267 zcmVk0APj~}1qIg|y;$4-|3eSdR=EXR#Ljv0&8i#El0bNq zCCPtbOc?zjLt;Ha9GISu$7A+MCe}mfMjVETgcv#JMftk%V+^MpZhzV1`Lv#fQTY|# zmVH%Kc?7GE@qgm|MuXXE5Wx|LA|lGXymX>j0mHBaiOCTeWuiGqm?tdkJb~nKEi%ds zbM!h!xMXZ&gS-Jn!ieZO!|zk!l0H6(3xx%l_7e^=V*nBnTyJoR3x(m7Y_~MkS+K|$ z43KnXp6`KzQc6$TjZ&Q@kr{*CB>m&JS3P*(6`NzwM1R5L6lTO&ICHWPon+|X`e7_z zMLC6F0kUhTIljIC`n@PVwP`u5k8%6kln44E_qKxh( zb{b06LXp^Yg%^cvtZ<1NCHc|QMWFQ-3U_9ArK2>SoN9MLNe(uuyLk@p)7%> zJlGHAXO1vp$Jbv&St9IU$JcM8FoX~@Q3#uw_NohXG+4jH|n z0sTU zpp0)pVW8-j`U)3UIhi+}#$BQ8($xh<8D1zSF{H#gGk+;3T}A$ClUFXto{2|3#opr< z1{e9RtJiwXxPn1_o!Y>j4vgs*Rnv1zdY19I z1T&QU(iw}qhn0M_M07d@(=B?wrnEYRR?F(SFg8$}3iw!f8+gbg+55IH7m6mdx_{;G zwJ5GVf)}rJh!K6BOpyHTY)Y&~5puDyja~ZueJI8j_G9+&*0hR%T$yseRbcqgywvf)bNZ*(}g~01Yys?kkB1~sKZ8{!H`|Y zbwYLk4a7gL^N~YT3{4p$1wFQ5i0U&(+omc9F}7{A?VAP;ziABgtA=yGYGnGW2HC&s d-wXbp{R5(SZUFku=BNMw002ovPDHLkV1n@6OJ4v0 diff --git a/examples/starting-first-person/assets/StarterToggle_Touch.png b/examples/starting-first-person/assets/StarterToggle_Touch.png index b38bab4da6aa9be1bf4487046f521efd39f403b6..204462798cece3a787bf29aaf3c77d2867382756 100644 GIT binary patch literal 3329 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 diff --git a/examples/starting-first-person/assets/StartingGround.png b/examples/starting-first-person/assets/StartingGround.png index 1c66f93412b0f53e8c73213298b347157759734a..edec42fec83b5ace1e1abd51c617a430c9a319be 100644 GIT binary patch literal 2824 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 delta 4495 zcmY+Ic{~%~|G?35%S|dr48@!ssF*Z4Gr2NX5{9`qWXYH#rOT0X4AGpCGvv$?W6Erf zzKF0`XpXX4{MPrM-|zRw^Lnn&^L$>P*Yn5w{d&@5N>rZ7>PPeQa&d9p{6Krh=>)

CVi05h7CM;*%6n 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 literal 378 zcmeAS@N?(olHy`uVBq!ia0vp^4Is?H1SEZ8zRdwrY)RhkE({(F?hNicYo9j)MK}vQ zB8wRqxP?KOkzv*x37{Z*iKnkC`ztmf9$D`E|670}jO?B+jv*Dd-rn8F*=iut7C2Q$ zP`v9mf8wv||V~00!*A{PeEu8xM;`w=bRd-)koZnsVy{m5C+^YZ2 z1Uh(uCNVKMFfeccO=M7DU|<0`QGhJLAMywO=-=p++8q2|e`Wn)>BE9c@^{)RRP6uF v-1ObLsfL*vmO$Mo$MBAyl|q5$ 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 FV1j1w)3N{n delta 627 zcmV-(0*w8$9DxOp8Gi!+006nq0-pc?05(ueR7Ff_aWyqHHa0dmI5;&mH8L_XHa0dg zGBP$cHa0dk|NsAAUtjX_^4{Lw#l^)&Mn->se~*ukx&LcZ0000AbW%=J0B>IZjxp~R zy4HO*rZ@lq010qNS#tmY3labT3lag+-G2N400G-cL_t(|+JD8F>ypAC4227X5~x;v z|ChDy&bnGlZe(`P@6Ii$Vi%H32h?z{` zA&({os!ck)jd&rN1@*kv&X;~w(7+>zIGK0^u?M`ITYobVd%(4ET6Mt2>z{${OJ<6^ zsnagq<8`6Sd|rA-EH^URbgQvHQ^O|E%x{@$LOs$9eVLZ5Rh; z*XQ!VgKQZc-=f2o!?Ed*2lmdfbz>*?te|XMb>FPA<&m<}EJWI#E9%{YoXZKS(#-Mi zEz^go>*bgJ1}U?hv%<;dkt!VPW>+x%4v0{XX>l^EM0ovUQ4t~&+a0mURTHt&FNy#F N002ovPDHLkV1nr&BUJzZ diff --git a/examples/starting-first-person/starting-first-person.json b/examples/starting-first-person/starting-first-person.json index bfb87d191..c6a046dca 100644 --- a/examples/starting-first-person/starting-first-person.json +++ b/examples/starting-first-person/starting-first-person.json @@ -1,7 +1,7 @@ { "firstLayout": "", "gdVersion": { - "build": 272, + "build": 274, "major": 5, "minor": 6, "revision": 0 @@ -349,13 +349,13 @@ "angle": 0, "customSize": true, "depth": 0, - "height": 120, + "height": 104, "keepRatio": true, "layer": "MobileControls", "locked": true, "name": "JumpButton", "persistentUuid": "c641f19e-29c5-4424-be1c-656b5505f1ac", - "width": 120, + "width": 104, "x": 1110, "y": 576, "zOrder": 8, @@ -819,11 +819,37 @@ "adaptCollisionMaskAutomatically": false, "assetStoreId": "9c727020616afdd6ba786b8af206a90481f07db0ca175ed6a4cc5b7e01c66d06", "name": "JumpButton", + "persistentUuid": "4d5343e5-eb14-488c-9bb9-1b7a057ee37c", "type": "Sprite", "updateIfNotVisible": false, "variables": [], "effects": [], "behaviors": [ + { + "name": "ButtonFSM", + "type": "ButtonStates::ButtonFSM", + "ShouldCheckHovering": true, + "State": "Idle", + "TouchId": 0, + "TouchIsInside": false, + "MouseIsInside": false, + "Index": 0 + }, + { + "name": "ButtonScaleTween", + "type": "ButtonStates::ButtonScaleTween", + "Scale": "Scale", + "ButtonFSM": "ButtonFSM", + "Tween": "Tween", + "IdleScale": 1.3, + "FocusedScale": 1.25, + "FadeInDuration": 0.02, + "FadeOutDuration": 0.05, + "PressedScale": 1.2, + "FadeInEasing": "easeOutQuad", + "FadeOutEasing": "easeInQuad", + "PreviousState": "Idle" + }, { "name": "MultitouchButton", "type": "SpriteMultitouchJoystick::MultitouchButton", @@ -832,6 +858,10 @@ "TouchId": 0, "TouchIndex": 2.0247e-320, "IsReleased": false + }, + { + "name": "Tween", + "type": "Tween::TweenBehavior" } ], "animations": [ @@ -1043,6 +1073,7 @@ { "assetStoreId": "", "name": "ControlsToggle", + "persistentUuid": "afafada9-ad2b-43f6-8c57-6d823dd5b5fc", "type": "SpriteToggleSwitch::SpriteToggleSwitch", "variant": "Controls toggle", "variables": [], @@ -1590,6 +1621,14 @@ "name": "Animation", "type": "AnimatableCapability::AnimatableBehavior" }, + { + "name": "ButtonFSM", + "type": "ButtonStates::ButtonFSM" + }, + { + "name": "ButtonScaleTween", + "type": "ButtonStates::ButtonScaleTween" + }, { "name": "Effect", "type": "EffectCapability::EffectBehavior" @@ -1645,6 +1684,10 @@ { "name": "Shooter3DMultitouchMapper", "type": "SpriteMultitouchJoystick::Shooter3DMultitouchMapper" + }, + { + "name": "Tween", + "type": "Tween::TweenBehavior" } ] } @@ -15743,6 +15786,7296 @@ ] } ] + }, + { + "author": "", + "category": "User interface", + "dimension": "2D", + "extensionNamespace": "", + "fullName": "Button states and effects", + "gdevelopVersion": ">=5.5.222", + "helpPath": "/objects/button", + "iconUrl": "data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iVVRGLTgiPz48IURPQ1RZUEUgc3ZnIFBVQkxJQyAiLS8vVzNDLy9EVEQgU1ZHIDEuMS8vRU4iICJodHRwOi8vd3d3LnczLm9yZy9HcmFwaGljcy9TVkcvMS4xL0RURC9zdmcxMS5kdGQiPjxzdmcgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIiB4bWxuczp4bGluaz0iaHR0cDovL3d3dy53My5vcmcvMTk5OS94bGluayIgdmVyc2lvbj0iMS4xIiBpZD0ibWRpLWdlc3R1cmUtdGFwLWJ1dHRvbiIgd2lkdGg9IjI0IiBoZWlnaHQ9IjI0IiB2aWV3Qm94PSIwIDAgMjQgMjQiPjxwYXRoIGQ9Ik0xMyA1QzE1LjIxIDUgMTcgNi43OSAxNyA5QzE3IDEwLjUgMTYuMiAxMS43NyAxNSAxMi40NlYxMS4yNEMxNS42MSAxMC42OSAxNiA5Ljg5IDE2IDlDMTYgNy4zNCAxNC42NiA2IDEzIDZTMTAgNy4zNCAxMCA5QzEwIDkuODkgMTAuMzkgMTAuNjkgMTEgMTEuMjRWMTIuNDZDOS44IDExLjc3IDkgMTAuNSA5IDlDOSA2Ljc5IDEwLjc5IDUgMTMgNU0yMCAyMC41QzE5Ljk3IDIxLjMyIDE5LjMyIDIxLjk3IDE4LjUgMjJIMTNDMTIuNjIgMjIgMTIuMjYgMjEuODUgMTIgMjEuNTdMOCAxNy4zN0w4Ljc0IDE2LjZDOC45MyAxNi4zOSA5LjIgMTYuMjggOS41IDE2LjI4SDkuN0wxMiAxOFY5QzEyIDguNDUgMTIuNDUgOCAxMyA4UzE0IDguNDUgMTQgOVYxMy40N0wxNS4yMSAxMy42TDE5LjE1IDE1Ljc5QzE5LjY4IDE2LjAzIDIwIDE2LjU2IDIwIDE3LjE0VjIwLjVNMjAgMkg0QzIuOSAyIDIgMi45IDIgNFYxMkMyIDEzLjExIDIuOSAxNCA0IDE0SDhWMTJMNCAxMkw0IDRIMjBMMjAgMTJIMThWMTRIMjBWMTMuOTZMMjAuMDQgMTRDMjEuMTMgMTQgMjIgMTMuMDkgMjIgMTJWNEMyMiAyLjkgMjEuMTEgMiAyMCAyWiIgLz48L3N2Zz4=", + "name": "ButtonStates", + "previewIconUrl": "https://asset-resources.gdevelop.io/public-resources/Icons/753a9a794bd885058159b7509f06f5a8f67f72decfccb9a1b0efee26f41c3c4c_gesture-tap-button.svg", + "shortDescription": "Use any object as a button and change appearance according to user interactions.", + "version": "1.4.1", + "description": [ + "Use the \"Button states\" behavior to track user interactions with an object, including:", + "", + "- Hovered", + "- Pressed", + "- Clicked", + "- Idle", + "", + "Add additional behaviors to make juicy buttons with animated responses to user input:", + "", + "- Size", + "- Color", + "- Animation", + "- Object effects" + ], + "origin": { + "identifier": "ButtonStates", + "name": "gdevelop-extension-store" + }, + "tags": [ + "ui", + "button" + ], + "authorIds": [ + "IWykYNRvhCZBN3vEgKEbBPOR3Oc2", + "gqDaZjCfevOOxBYkK6zlhtZnXCg1" + ], + "dependencies": [], + "globalVariables": [], + "sceneVariables": [], + "eventsFunctions": [], + "eventsFunctionsFolderStructure": { + "folderName": "__ROOT" + }, + "eventsBasedBehaviors": [ + { + "description": "Use objects as buttons.", + "fullName": "Button states", + "helpPath": "", + "iconUrl": "", + "name": "ButtonFSM", + "objectType": "", + "previewIconUrl": "", + "eventsFunctions": [ + { + "fullName": "", + "functionType": "Action", + "name": "doStepPreEvents", + "sentence": "", + "events": [ + { + "colorB": 228, + "colorG": 176, + "colorR": 74, + "creationTime": 0, + "name": "Finite state machine", + "source": "", + "type": "BuiltinCommonInstructions::Group", + "events": [ + { + "type": "BuiltinCommonInstructions::Comment", + "color": { + "b": 109, + "g": 230, + "r": 255, + "textB": 0, + "textG": 0, + "textR": 0 + }, + "comment": "The \"Validated\" state only last one frame." + }, + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [ + { + "type": { + "value": "StringVariable" + }, + "parameters": [ + "State", + "=", + "\"Validated\"" + ] + } + ], + "actions": [ + { + "type": { + "value": "SetStringVariable" + }, + "parameters": [ + "State", + "=", + "\"Idle\"" + ] + } + ] + }, + { + "colorB": 228, + "colorG": 176, + "colorR": 74, + "creationTime": 0, + "name": "Check position", + "source": "", + "type": "BuiltinCommonInstructions::Group", + "events": [ + { + "type": "BuiltinCommonInstructions::Comment", + "color": { + "b": 109, + "g": 230, + "r": 255, + "textB": 0, + "textG": 0, + "textR": 0 + }, + "comment": "Make sure the cursor position is only checked once per frame." + }, + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [], + "actions": [ + { + "type": { + "value": "SetBooleanVariable" + }, + "parameters": [ + "MouseIsInside", + "False", + "" + ] + } + ] + }, + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [ + { + "type": { + "value": "BooleanVariable" + }, + "parameters": [ + "ShouldCheckHovering", + "True", + "" + ] + }, + { + "type": { + "value": "CollisionPoint" + }, + "parameters": [ + "Object", + "MouseOnlyCursorX(Object.Layer(), 0)", + "MouseOnlyCursorY(Object.Layer(), 0)" + ] + } + ], + "actions": [ + { + "type": { + "value": "SetBooleanVariable" + }, + "parameters": [ + "MouseIsInside", + "True", + "" + ] + } + ] + }, + { + "type": "BuiltinCommonInstructions::Comment", + "color": { + "b": 109, + "g": 230, + "r": 255, + "textB": 0, + "textG": 0, + "textR": 0 + }, + "comment": "Touches are always pressed, so ShouldCheckHovering doesn't matter." + }, + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [], + "actions": [ + { + "type": { + "value": "SetBooleanVariable" + }, + "parameters": [ + "TouchIsInside", + "False", + "" + ] + } + ] + }, + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [ + { + "type": { + "value": "NumberVariable" + }, + "parameters": [ + "TouchId", + "!=", + "0" + ] + }, + { + "type": { + "value": "CollisionPoint" + }, + "parameters": [ + "Object", + "TouchX(TouchId, Object.Layer(), 0)", + "TouchY(TouchId, Object.Layer(), 0)" + ] + } + ], + "actions": [ + { + "type": { + "value": "SetBooleanVariable" + }, + "parameters": [ + "TouchIsInside", + "True", + "" + ] + } + ] + } + ], + "parameters": [] + }, + { + "colorB": 228, + "colorG": 176, + "colorR": 74, + "creationTime": 0, + "name": "Handle touch start", + "source": "", + "type": "BuiltinCommonInstructions::Group", + "events": [ + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [ + { + "type": { + "value": "HasAnyTouchOrMouseStarted" + }, + "parameters": [ + "" + ] + } + ], + "actions": [ + { + "type": { + "value": "SetNumberVariable" + }, + "parameters": [ + "Index", + "=", + "0" + ] + } + ], + "events": [ + { + "type": "BuiltinCommonInstructions::Repeat", + "repeatExpression": "StartedTouchOrMouseCount()", + "conditions": [], + "actions": [], + "events": [ + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [ + { + "type": { + "value": "CollisionPoint" + }, + "parameters": [ + "Object", + "TouchX(StartedTouchOrMouseId(Index), Object.Layer(), 0)", + "TouchY(StartedTouchOrMouseId(Index), Object.Layer(), 0)" + ] + } + ], + "actions": [ + { + "type": { + "value": "SetNumberVariable" + }, + "parameters": [ + "TouchId", + "=", + "StartedTouchOrMouseId(Index)" + ] + }, + { + "type": { + "value": "SetBooleanVariable" + }, + "parameters": [ + "TouchIsInside", + "True", + "" + ] + } + ], + "events": [ + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [ + { + "type": { + "value": "BuiltinCommonInstructions::Or" + }, + "parameters": [], + "subInstructions": [ + { + "type": { + "value": "StringVariable" + }, + "parameters": [ + "State", + "=", + "\"Hovered\"" + ] + }, + { + "type": { + "value": "StringVariable" + }, + "parameters": [ + "State", + "=", + "\"Idle\"" + ] + } + ] + } + ], + "actions": [ + { + "type": { + "value": "SetStringVariable" + }, + "parameters": [ + "State", + "=", + "\"PressedInside\"" + ] + } + ] + } + ] + }, + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [], + "actions": [ + { + "type": { + "value": "SetNumberVariable" + }, + "parameters": [ + "Index", + "+", + "1" + ] + } + ] + } + ] + } + ] + } + ], + "parameters": [] + }, + { + "colorB": 228, + "colorG": 176, + "colorR": 74, + "creationTime": 0, + "name": "Apply position changes", + "source": "", + "type": "BuiltinCommonInstructions::Group", + "events": [ + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [ + { + "type": { + "inverted": true, + "value": "BooleanVariable" + }, + "parameters": [ + "MouseIsInside", + "True", + "" + ] + }, + { + "type": { + "value": "StringVariable" + }, + "parameters": [ + "State", + "=", + "\"Hovered\"" + ] + } + ], + "actions": [ + { + "type": { + "value": "SetStringVariable" + }, + "parameters": [ + "State", + "=", + "\"Idle\"" + ] + } + ] + }, + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [ + { + "type": { + "value": "BooleanVariable" + }, + "parameters": [ + "MouseIsInside", + "True", + "" + ] + }, + { + "type": { + "value": "StringVariable" + }, + "parameters": [ + "State", + "=", + "\"Idle\"" + ] + } + ], + "actions": [ + { + "type": { + "value": "SetStringVariable" + }, + "parameters": [ + "State", + "=", + "\"Hovered\"" + ] + } + ] + }, + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [ + { + "type": { + "inverted": true, + "value": "BooleanVariable" + }, + "parameters": [ + "TouchIsInside", + "True", + "" + ] + }, + { + "type": { + "value": "StringVariable" + }, + "parameters": [ + "State", + "=", + "\"PressedInside\"" + ] + } + ], + "actions": [ + { + "type": { + "value": "SetStringVariable" + }, + "parameters": [ + "State", + "=", + "\"PressedOutside\"" + ] + } + ] + }, + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [ + { + "type": { + "value": "BooleanVariable" + }, + "parameters": [ + "TouchIsInside", + "True", + "" + ] + }, + { + "type": { + "value": "StringVariable" + }, + "parameters": [ + "State", + "=", + "\"PressedOutside\"" + ] + } + ], + "actions": [ + { + "type": { + "value": "SetStringVariable" + }, + "parameters": [ + "State", + "=", + "\"PressedInside\"" + ] + } + ] + } + ], + "parameters": [] + }, + { + "colorB": 228, + "colorG": 176, + "colorR": 74, + "creationTime": 0, + "name": "Handle touch end", + "source": "", + "type": "BuiltinCommonInstructions::Group", + "events": [ + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [ + { + "type": { + "value": "HasTouchEnded" + }, + "parameters": [ + "", + "TouchId" + ] + } + ], + "actions": [ + { + "type": { + "value": "SetNumberVariable" + }, + "parameters": [ + "TouchId", + "=", + "0" + ] + } + ], + "events": [ + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [ + { + "type": { + "value": "StringVariable" + }, + "parameters": [ + "State", + "=", + "\"PressedInside\"" + ] + } + ], + "actions": [ + { + "type": { + "value": "SetStringVariable" + }, + "parameters": [ + "State", + "=", + "\"Validated\"" + ] + } + ] + }, + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [ + { + "type": { + "inverted": true, + "value": "StringVariable" + }, + "parameters": [ + "State", + "=", + "\"PressedInside\"" + ] + }, + { + "type": { + "inverted": true, + "value": "StringVariable" + }, + "parameters": [ + "State", + "=", + "\"Validated\"" + ] + } + ], + "actions": [ + { + "type": { + "value": "SetStringVariable" + }, + "parameters": [ + "State", + "=", + "\"Idle\"" + ] + } + ] + } + ] + } + ], + "parameters": [] + } + ], + "parameters": [] + } + ], + "parameters": [ + { + "description": "Object", + "name": "Object", + "type": "object" + }, + { + "description": "Behavior", + "name": "Behavior", + "supplementaryInformation": "ButtonStates::ButtonFSM", + "type": "behavior" + } + ], + "objectGroups": [] + }, + { + "fullName": "", + "functionType": "Action", + "name": "onDeActivate", + "sentence": "", + "events": [ + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [], + "actions": [ + { + "type": { + "value": "ButtonStates::ButtonFSM::ResetState" + }, + "parameters": [ + "Object", + "Behavior", + "" + ] + } + ] + } + ], + "parameters": [ + { + "description": "Object", + "name": "Object", + "type": "object" + }, + { + "description": "Behavior", + "name": "Behavior", + "supplementaryInformation": "ButtonStates::ButtonFSM", + "type": "behavior" + } + ], + "objectGroups": [] + }, + { + "description": "Reset the state of the button.", + "fullName": "Reset state", + "functionType": "Action", + "name": "ResetState", + "private": true, + "sentence": "Reset the button state of _PARAM0_", + "events": [ + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [], + "actions": [ + { + "type": { + "value": "SetStringVariable" + }, + "parameters": [ + "State", + "=", + "\"Idle\"" + ] + }, + { + "type": { + "value": "SetNumberVariable" + }, + "parameters": [ + "TouchId", + "=", + "0" + ] + } + ] + } + ], + "parameters": [ + { + "description": "Object", + "name": "Object", + "type": "object" + }, + { + "description": "Behavior", + "name": "Behavior", + "supplementaryInformation": "ButtonStates::ButtonFSM", + "type": "behavior" + } + ], + "objectGroups": [] + }, + { + "description": "Check if the button is not used.", + "fullName": "Is idle", + "functionType": "Condition", + "name": "IsIdle", + "sentence": "_PARAM0_ is idle", + "events": [ + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [ + { + "type": { + "value": "StringVariable" + }, + "parameters": [ + "State", + "=", + "\"Idle\"" + ] + } + ], + "actions": [ + { + "type": { + "value": "SetReturnBoolean" + }, + "parameters": [ + "True" + ] + } + ] + } + ], + "parameters": [ + { + "description": "Object", + "name": "Object", + "type": "object" + }, + { + "description": "Behavior", + "name": "Behavior", + "supplementaryInformation": "ButtonStates::ButtonFSM", + "type": "behavior" + } + ], + "objectGroups": [] + }, + { + "description": "Check if the button was just clicked.", + "fullName": "Is clicked", + "functionType": "Condition", + "name": "IsClicked", + "sentence": "_PARAM0_ is clicked", + "events": [ + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [ + { + "type": { + "value": "StringVariable" + }, + "parameters": [ + "State", + "=", + "\"Validated\"" + ] + } + ], + "actions": [ + { + "type": { + "value": "SetReturnBoolean" + }, + "parameters": [ + "True" + ] + } + ] + } + ], + "parameters": [ + { + "description": "Object", + "name": "Object", + "type": "object" + }, + { + "description": "Behavior", + "name": "Behavior", + "supplementaryInformation": "ButtonStates::ButtonFSM", + "type": "behavior" + } + ], + "objectGroups": [] + }, + { + "description": "Check if the cursor is hovered over the button.", + "fullName": "Is hovered", + "functionType": "Condition", + "name": "IsHovered", + "sentence": "_PARAM0_ is hovered", + "events": [ + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [ + { + "type": { + "value": "StringVariable" + }, + "parameters": [ + "State", + "=", + "\"Hovered\"" + ] + } + ], + "actions": [ + { + "type": { + "value": "SetReturnBoolean" + }, + "parameters": [ + "True" + ] + } + ] + } + ], + "parameters": [ + { + "description": "Object", + "name": "Object", + "type": "object" + }, + { + "description": "Behavior", + "name": "Behavior", + "supplementaryInformation": "ButtonStates::ButtonFSM", + "type": "behavior" + } + ], + "objectGroups": [] + }, + { + "description": "Check if the button is either hovered or pressed but not hovered.", + "fullName": "Is focused", + "functionType": "Condition", + "name": "IsFocused", + "sentence": "_PARAM0_ is focused", + "events": [ + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [ + { + "type": { + "value": "StringVariable" + }, + "parameters": [ + "State", + "=", + "\"Hovered\"" + ] + } + ], + "actions": [ + { + "type": { + "value": "SetReturnBoolean" + }, + "parameters": [ + "True" + ] + } + ] + }, + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [ + { + "type": { + "value": "StringVariable" + }, + "parameters": [ + "State", + "=", + "\"PressedOutside\"" + ] + } + ], + "actions": [ + { + "type": { + "value": "SetReturnBoolean" + }, + "parameters": [ + "True" + ] + } + ] + } + ], + "parameters": [ + { + "description": "Object", + "name": "Object", + "type": "object" + }, + { + "description": "Behavior", + "name": "Behavior", + "supplementaryInformation": "ButtonStates::ButtonFSM", + "type": "behavior" + } + ], + "objectGroups": [] + }, + { + "description": "Check if the button is currently being pressed with mouse or touch.", + "fullName": "Is pressed", + "functionType": "Condition", + "name": "IsPressed", + "sentence": "_PARAM0_ is pressed", + "events": [ + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [ + { + "type": { + "value": "StringVariable" + }, + "parameters": [ + "State", + "=", + "\"PressedInside\"" + ] + } + ], + "actions": [ + { + "type": { + "value": "SetReturnBoolean" + }, + "parameters": [ + "True" + ] + } + ] + } + ], + "parameters": [ + { + "description": "Object", + "name": "Object", + "type": "object" + }, + { + "description": "Behavior", + "name": "Behavior", + "supplementaryInformation": "ButtonStates::ButtonFSM", + "type": "behavior" + } + ], + "objectGroups": [] + }, + { + "description": "Check if the button is currently being pressed outside with mouse or touch.", + "fullName": "Is held outside", + "functionType": "Condition", + "name": "IsPressedOutside", + "sentence": "_PARAM0_ is held outside", + "events": [ + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [ + { + "type": { + "value": "StringVariable" + }, + "parameters": [ + "State", + "=", + "\"PressedOutside\"" + ] + } + ], + "actions": [ + { + "type": { + "value": "SetReturnBoolean" + }, + "parameters": [ + "True" + ] + } + ] + } + ], + "parameters": [ + { + "description": "Object", + "name": "Object", + "type": "object" + }, + { + "description": "Behavior", + "name": "Behavior", + "supplementaryInformation": "ButtonStates::ButtonFSM", + "type": "behavior" + } + ], + "objectGroups": [] + }, + { + "description": "the touch id that is using the button or 0 if none.", + "fullName": "Touch id", + "functionType": "ExpressionAndCondition", + "name": "TouchId", + "sentence": "the touch id", + "events": [ + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [], + "actions": [ + { + "type": { + "value": "SetReturnNumber" + }, + "parameters": [ + "TouchId" + ] + } + ] + } + ], + "expressionType": { + "type": "expression" + }, + "parameters": [ + { + "description": "Object", + "name": "Object", + "type": "object" + }, + { + "description": "Behavior", + "name": "Behavior", + "supplementaryInformation": "ButtonStates::ButtonFSM", + "type": "behavior" + } + ], + "objectGroups": [] + } + ], + "eventsFunctionsFolderStructure": { + "folderName": "__ROOT", + "children": [ + { + "functionName": "doStepPreEvents" + }, + { + "functionName": "onDeActivate" + }, + { + "functionName": "ResetState" + }, + { + "functionName": "IsIdle" + }, + { + "functionName": "IsClicked" + }, + { + "functionName": "IsHovered" + }, + { + "functionName": "IsFocused" + }, + { + "functionName": "IsPressed" + }, + { + "functionName": "IsPressedOutside" + }, + { + "functionName": "TouchId" + } + ] + }, + "propertyDescriptors": [ + { + "value": "true", + "type": "Boolean", + "label": "", + "description": "Should check hovering", + "hidden": true, + "name": "ShouldCheckHovering" + }, + { + "value": "Idle", + "type": "Choice", + "label": "State", + "choices": [ + { + "label": "Idle", + "value": "Idle" + }, + { + "label": "Hovered", + "value": "Hovered" + }, + { + "label": "PressedInside", + "value": "PressedInside" + }, + { + "label": "PressedOutside", + "value": "PressedOutside" + }, + { + "label": "Validated", + "value": "Validated" + } + ], + "hidden": true, + "name": "State" + }, + { + "value": "0", + "type": "Number", + "label": "Touch id", + "hidden": true, + "name": "TouchId" + }, + { + "value": "", + "type": "Boolean", + "label": "Touch is inside", + "hidden": true, + "name": "TouchIsInside" + }, + { + "value": "", + "type": "Boolean", + "label": "Mouse is inside", + "hidden": true, + "name": "MouseIsInside" + }, + { + "value": "", + "type": "Number", + "label": "", + "hidden": true, + "name": "Index" + } + ], + "propertiesFolderStructure": { + "folderName": "__ROOT", + "children": [ + { + "propertyName": "ShouldCheckHovering" + }, + { + "propertyName": "State" + }, + { + "propertyName": "TouchId" + }, + { + "propertyName": "TouchIsInside" + }, + { + "propertyName": "MouseIsInside" + }, + { + "propertyName": "Index" + } + ] + } + }, + { + "description": "Enable effects on buttons based on their state.", + "fullName": "Button object effects", + "helpPath": "", + "iconUrl": "", + "name": "ButtonObjectEffects", + "objectType": "", + "previewIconUrl": "", + "eventsFunctions": [ + { + "fullName": "", + "functionType": "Action", + "name": "onCreated", + "sentence": "", + "events": [ + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [], + "actions": [ + { + "type": { + "value": "EffectCapability::EffectBehavior::EnableEffect" + }, + "parameters": [ + "Object", + "Effect", + "IdleEffect", + "yes" + ] + }, + { + "type": { + "value": "EffectCapability::EffectBehavior::EnableEffect" + }, + "parameters": [ + "Object", + "Effect", + "FocusedEffect", + "no" + ] + }, + { + "type": { + "value": "EffectCapability::EffectBehavior::EnableEffect" + }, + "parameters": [ + "Object", + "Effect", + "PressedEffect", + "no" + ] + } + ] + } + ], + "parameters": [ + { + "description": "Object", + "name": "Object", + "type": "object" + }, + { + "description": "Behavior", + "name": "Behavior", + "supplementaryInformation": "ButtonStates::ButtonObjectEffects", + "type": "behavior" + } + ], + "objectGroups": [] + }, + { + "fullName": "", + "functionType": "Action", + "name": "doStepPreEvents", + "sentence": "", + "events": [ + { + "colorB": 228, + "colorG": 176, + "colorR": 74, + "creationTime": 0, + "name": "States", + "source": "", + "type": "BuiltinCommonInstructions::Group", + "events": [ + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [], + "actions": [ + { + "type": { + "value": "EffectCapability::EffectBehavior::EnableEffect" + }, + "parameters": [ + "Object", + "Effect", + "IdleEffect", + "no" + ] + }, + { + "type": { + "value": "EffectCapability::EffectBehavior::EnableEffect" + }, + "parameters": [ + "Object", + "Effect", + "FocusedEffect", + "no" + ] + }, + { + "type": { + "value": "EffectCapability::EffectBehavior::EnableEffect" + }, + "parameters": [ + "Object", + "Effect", + "PressedEffect", + "no" + ] + } + ] + }, + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [ + { + "type": { + "value": "StringVariable" + }, + "parameters": [ + "IdleEffect", + "!=", + "\"\"" + ] + }, + { + "type": { + "value": "ButtonStates::ButtonFSM::IsIdle" + }, + "parameters": [ + "Object", + "ButtonFSM", + "" + ] + } + ], + "actions": [ + { + "type": { + "value": "EffectCapability::EffectBehavior::EnableEffect" + }, + "parameters": [ + "Object", + "Effect", + "IdleEffect", + "yes" + ] + } + ] + }, + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [ + { + "type": { + "value": "StringVariable" + }, + "parameters": [ + "FocusedEffect", + "!=", + "\"\"" + ] + }, + { + "type": { + "value": "ButtonStates::ButtonFSM::IsFocused" + }, + "parameters": [ + "Object", + "ButtonFSM", + "" + ] + } + ], + "actions": [ + { + "type": { + "value": "EffectCapability::EffectBehavior::EnableEffect" + }, + "parameters": [ + "Object", + "Effect", + "FocusedEffect", + "yes" + ] + } + ] + }, + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [ + { + "type": { + "value": "StringVariable" + }, + "parameters": [ + "PressedEffect", + "!=", + "\"\"" + ] + }, + { + "type": { + "value": "ButtonStates::ButtonFSM::IsPressed" + }, + "parameters": [ + "Object", + "ButtonFSM", + "" + ] + } + ], + "actions": [ + { + "type": { + "value": "EffectCapability::EffectBehavior::EnableEffect" + }, + "parameters": [ + "Object", + "Effect", + "PressedEffect", + "yes" + ] + } + ] + } + ], + "parameters": [] + } + ], + "parameters": [ + { + "description": "Object", + "name": "Object", + "type": "object" + }, + { + "description": "Behavior", + "name": "Behavior", + "supplementaryInformation": "ButtonStates::ButtonObjectEffects", + "type": "behavior" + } + ], + "objectGroups": [] + }, + { + "description": "the idle state effect of the object.", + "fullName": "Idle state effect", + "functionType": "ExpressionAndCondition", + "group": "Button object effects configuration", + "name": "IdleEffect", + "sentence": "the idle state effect", + "events": [ + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [], + "actions": [ + { + "type": { + "value": "SetReturnString" + }, + "parameters": [ + "IdleEffect" + ] + } + ] + } + ], + "expressionType": { + "type": "string" + }, + "parameters": [ + { + "description": "Object", + "name": "Object", + "type": "object" + }, + { + "description": "Behavior", + "name": "Behavior", + "supplementaryInformation": "ButtonStates::ButtonObjectEffects", + "type": "behavior" + } + ], + "objectGroups": [] + }, + { + "fullName": "", + "functionType": "ActionWithOperator", + "getterName": "IdleEffect", + "group": "Button object effects configuration", + "name": "SetIdleEffect", + "sentence": "", + "events": [ + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [], + "actions": [ + { + "type": { + "value": "SetStringVariable" + }, + "parameters": [ + "IdleEffect", + "=", + "Value" + ] + } + ] + } + ], + "parameters": [ + { + "description": "Object", + "name": "Object", + "type": "object" + }, + { + "description": "Behavior", + "name": "Behavior", + "supplementaryInformation": "ButtonStates::ButtonObjectEffects", + "type": "behavior" + } + ], + "objectGroups": [] + }, + { + "description": "the focused state effect of the object. The state is Focused when the button is hovered or held outside.", + "fullName": "Focused state effect", + "functionType": "ExpressionAndCondition", + "group": "Button object effects configuration", + "name": "FocusedEffect", + "sentence": "the focused state effect", + "events": [ + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [], + "actions": [ + { + "type": { + "value": "SetReturnString" + }, + "parameters": [ + "FocusedEffect" + ] + } + ] + } + ], + "expressionType": { + "type": "string" + }, + "parameters": [ + { + "description": "Object", + "name": "Object", + "type": "object" + }, + { + "description": "Behavior", + "name": "Behavior", + "supplementaryInformation": "ButtonStates::ButtonObjectEffects", + "type": "behavior" + } + ], + "objectGroups": [] + }, + { + "fullName": "", + "functionType": "ActionWithOperator", + "getterName": "FocusedEffect", + "group": "Button object effects configuration", + "name": "SetFocusedEffect", + "sentence": "", + "events": [ + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [], + "actions": [ + { + "type": { + "value": "SetStringVariable" + }, + "parameters": [ + "FocusedEffect", + "=", + "Value" + ] + } + ] + } + ], + "parameters": [ + { + "description": "Object", + "name": "Object", + "type": "object" + }, + { + "description": "Behavior", + "name": "Behavior", + "supplementaryInformation": "ButtonStates::ButtonObjectEffects", + "type": "behavior" + } + ], + "objectGroups": [] + }, + { + "description": "the pressed state effect of the object.", + "fullName": "Pressed state effect", + "functionType": "ExpressionAndCondition", + "group": "Button object effects configuration", + "name": "PressedEffect", + "sentence": "the pressed state effect", + "events": [ + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [], + "actions": [ + { + "type": { + "value": "SetReturnString" + }, + "parameters": [ + "PressedEffect" + ] + } + ] + } + ], + "expressionType": { + "type": "string" + }, + "parameters": [ + { + "description": "Object", + "name": "Object", + "type": "object" + }, + { + "description": "Behavior", + "name": "Behavior", + "supplementaryInformation": "ButtonStates::ButtonObjectEffects", + "type": "behavior" + } + ], + "objectGroups": [] + }, + { + "fullName": "", + "functionType": "ActionWithOperator", + "getterName": "PressedEffect", + "group": "Button object effects configuration", + "name": "SetPressedEffect", + "sentence": "", + "events": [ + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [], + "actions": [ + { + "type": { + "value": "SetStringVariable" + }, + "parameters": [ + "PressedEffect", + "=", + "Value" + ] + } + ] + } + ], + "parameters": [ + { + "description": "Object", + "name": "Object", + "type": "object" + }, + { + "description": "Behavior", + "name": "Behavior", + "supplementaryInformation": "ButtonStates::ButtonObjectEffects", + "type": "behavior" + } + ], + "objectGroups": [] + } + ], + "eventsFunctionsFolderStructure": { + "folderName": "__ROOT", + "children": [ + { + "functionName": "onCreated" + }, + { + "functionName": "doStepPreEvents" + }, + { + "folderName": "Button object effects configuration", + "children": [ + { + "functionName": "IdleEffect" + }, + { + "functionName": "SetIdleEffect" + }, + { + "functionName": "FocusedEffect" + }, + { + "functionName": "SetFocusedEffect" + }, + { + "functionName": "PressedEffect" + }, + { + "functionName": "SetPressedEffect" + } + ] + } + ] + }, + "propertyDescriptors": [ + { + "value": "", + "type": "Behavior", + "label": "Button states", + "extraInformation": [ + "ButtonStates::ButtonFSM" + ], + "choices": [], + "name": "ButtonFSM" + }, + { + "value": "", + "type": "Behavior", + "label": "Effect capability", + "extraInformation": [ + "EffectCapability::EffectBehavior" + ], + "choices": [], + "name": "Effect" + }, + { + "value": "", + "type": "String", + "label": "Idle state effect", + "group": "Effects", + "name": "IdleEffect" + }, + { + "value": "", + "type": "String", + "label": "Focused state effect", + "description": "The state is Focused when the button is hovered or held outside.", + "group": "Effects", + "name": "FocusedEffect" + }, + { + "value": "", + "type": "String", + "label": "Pressed state effect", + "group": "Effects", + "name": "PressedEffect" + } + ], + "propertiesFolderStructure": { + "folderName": "__ROOT", + "children": [ + { + "propertyName": "ButtonFSM" + }, + { + "propertyName": "Effect" + }, + { + "folderName": "Effects", + "children": [ + { + "propertyName": "IdleEffect" + }, + { + "propertyName": "FocusedEffect" + }, + { + "propertyName": "PressedEffect" + } + ] + } + ] + } + }, + { + "description": "Change the animation of buttons according to their state.", + "fullName": "Button animation", + "helpPath": "", + "iconUrl": "", + "name": "ButtonAnimationName", + "objectType": "", + "previewIconUrl": "", + "eventsFunctions": [ + { + "fullName": "", + "functionType": "Action", + "name": "onCreated", + "sentence": "", + "events": [ + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [], + "actions": [ + { + "type": { + "value": "AnimatableCapability::AnimatableBehavior::SetName" + }, + "parameters": [ + "Object", + "Animation", + "=", + "IdleAnimationName" + ] + } + ] + } + ], + "parameters": [ + { + "description": "Object", + "name": "Object", + "type": "object" + }, + { + "description": "Behavior", + "name": "Behavior", + "supplementaryInformation": "ButtonStates::ButtonAnimationName", + "type": "behavior" + } + ], + "objectGroups": [] + }, + { + "fullName": "", + "functionType": "Action", + "name": "doStepPreEvents", + "sentence": "", + "events": [ + { + "colorB": 228, + "colorG": 176, + "colorR": 74, + "creationTime": 0, + "name": "Change based on state", + "source": "", + "type": "BuiltinCommonInstructions::Group", + "events": [ + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [ + { + "type": { + "value": "ButtonStates::ButtonFSM::IsIdle" + }, + "parameters": [ + "Object", + "ButtonFSM", + "" + ] + } + ], + "actions": [ + { + "type": { + "value": "AnimatableCapability::AnimatableBehavior::SetName" + }, + "parameters": [ + "Object", + "Animation", + "=", + "IdleAnimationName" + ] + } + ] + }, + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [ + { + "type": { + "value": "ButtonStates::ButtonFSM::IsFocused" + }, + "parameters": [ + "Object", + "ButtonFSM", + "" + ] + } + ], + "actions": [ + { + "type": { + "value": "AnimatableCapability::AnimatableBehavior::SetName" + }, + "parameters": [ + "Object", + "Animation", + "=", + "FocusedAnimationName" + ] + } + ] + }, + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [ + { + "type": { + "value": "ButtonStates::ButtonFSM::IsPressed" + }, + "parameters": [ + "Object", + "ButtonFSM", + "" + ] + } + ], + "actions": [ + { + "type": { + "value": "AnimatableCapability::AnimatableBehavior::SetName" + }, + "parameters": [ + "Object", + "Animation", + "=", + "PressedAnimationName" + ] + } + ] + } + ], + "parameters": [] + } + ], + "parameters": [ + { + "description": "Object", + "name": "Object", + "type": "object" + }, + { + "description": "Behavior", + "name": "Behavior", + "supplementaryInformation": "ButtonStates::ButtonAnimationName", + "type": "behavior" + } + ], + "objectGroups": [] + }, + { + "description": "the idle state animation name of the object.", + "fullName": "Idle state animation name", + "functionType": "ExpressionAndCondition", + "group": "Button animation configuration", + "name": "IdleAnimationName", + "sentence": "the idle state animation name", + "events": [ + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [], + "actions": [ + { + "type": { + "value": "SetReturnString" + }, + "parameters": [ + "IdleAnimationName" + ] + } + ] + } + ], + "expressionType": { + "type": "string" + }, + "parameters": [ + { + "description": "Object", + "name": "Object", + "type": "object" + }, + { + "description": "Behavior", + "name": "Behavior", + "supplementaryInformation": "ButtonStates::ButtonAnimationName", + "type": "behavior" + } + ], + "objectGroups": [] + }, + { + "fullName": "", + "functionType": "ActionWithOperator", + "getterName": "IdleAnimationName", + "group": "Button animation configuration", + "name": "SetIdleAnimationName", + "sentence": "", + "events": [ + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [], + "actions": [ + { + "type": { + "value": "SetStringVariable" + }, + "parameters": [ + "IdleAnimationName", + "=", + "Value" + ] + } + ] + } + ], + "parameters": [ + { + "description": "Object", + "name": "Object", + "type": "object" + }, + { + "description": "Behavior", + "name": "Behavior", + "supplementaryInformation": "ButtonStates::ButtonAnimationName", + "type": "behavior" + } + ], + "objectGroups": [] + }, + { + "description": "the focused state animation name of the object. The state is Focused when the button is hovered or held outside.", + "fullName": "Focused state animation name", + "functionType": "ExpressionAndCondition", + "group": "Button animation configuration", + "name": "FocusedAnimationName", + "sentence": "the focused state animation name", + "events": [ + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [], + "actions": [ + { + "type": { + "value": "SetReturnString" + }, + "parameters": [ + "FocusedAnimationName" + ] + } + ] + } + ], + "expressionType": { + "type": "string" + }, + "parameters": [ + { + "description": "Object", + "name": "Object", + "type": "object" + }, + { + "description": "Behavior", + "name": "Behavior", + "supplementaryInformation": "ButtonStates::ButtonAnimationName", + "type": "behavior" + } + ], + "objectGroups": [] + }, + { + "fullName": "", + "functionType": "ActionWithOperator", + "getterName": "FocusedAnimationName", + "group": "Button animation configuration", + "name": "SetFocusedAnimationName", + "sentence": "", + "events": [ + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [], + "actions": [ + { + "type": { + "value": "SetStringVariable" + }, + "parameters": [ + "FocusedAnimationName", + "=", + "Value" + ] + } + ] + } + ], + "parameters": [ + { + "description": "Object", + "name": "Object", + "type": "object" + }, + { + "description": "Behavior", + "name": "Behavior", + "supplementaryInformation": "ButtonStates::ButtonAnimationName", + "type": "behavior" + } + ], + "objectGroups": [] + }, + { + "description": "the pressed state animation name of the object.", + "fullName": "Pressed state animation name", + "functionType": "ExpressionAndCondition", + "group": "Button animation configuration", + "name": "PressedAnimationName", + "sentence": "the pressed state animation name", + "events": [ + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [], + "actions": [ + { + "type": { + "value": "SetReturnString" + }, + "parameters": [ + "PressedAnimationName" + ] + } + ] + } + ], + "expressionType": { + "type": "string" + }, + "parameters": [ + { + "description": "Object", + "name": "Object", + "type": "object" + }, + { + "description": "Behavior", + "name": "Behavior", + "supplementaryInformation": "ButtonStates::ButtonAnimationName", + "type": "behavior" + } + ], + "objectGroups": [] + }, + { + "fullName": "", + "functionType": "ActionWithOperator", + "getterName": "PressedAnimationName", + "group": "Button animation configuration", + "name": "SetPressedAnimationName", + "sentence": "", + "events": [ + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [], + "actions": [ + { + "type": { + "value": "SetStringVariable" + }, + "parameters": [ + "PressedAnimationName", + "=", + "Value" + ] + } + ] + } + ], + "parameters": [ + { + "description": "Object", + "name": "Object", + "type": "object" + }, + { + "description": "Behavior", + "name": "Behavior", + "supplementaryInformation": "ButtonStates::ButtonAnimationName", + "type": "behavior" + } + ], + "objectGroups": [] + } + ], + "eventsFunctionsFolderStructure": { + "folderName": "__ROOT", + "children": [ + { + "functionName": "onCreated" + }, + { + "functionName": "doStepPreEvents" + }, + { + "folderName": "Button animation configuration", + "children": [ + { + "functionName": "IdleAnimationName" + }, + { + "functionName": "SetIdleAnimationName" + }, + { + "functionName": "FocusedAnimationName" + }, + { + "functionName": "SetFocusedAnimationName" + }, + { + "functionName": "PressedAnimationName" + }, + { + "functionName": "SetPressedAnimationName" + } + ] + } + ] + }, + "propertyDescriptors": [ + { + "value": "", + "type": "Behavior", + "label": "Animatable capability", + "extraInformation": [ + "AnimatableCapability::AnimatableBehavior" + ], + "choices": [], + "name": "Animation" + }, + { + "value": "", + "type": "Behavior", + "label": "Button states", + "extraInformation": [ + "ButtonStates::ButtonFSM" + ], + "choices": [], + "name": "ButtonFSM" + }, + { + "value": "Idle", + "type": "String", + "label": "Idle state animation name", + "group": "Animation", + "name": "IdleAnimationName" + }, + { + "value": "Focused", + "type": "String", + "label": "Focused state animation name", + "description": "The state is Focused when the button is hovered or held outside.", + "group": "Animation", + "name": "FocusedAnimationName" + }, + { + "value": "Pressed", + "type": "String", + "label": "Pressed state animation name", + "group": "Animation", + "name": "PressedAnimationName" + } + ], + "propertiesFolderStructure": { + "folderName": "__ROOT", + "children": [ + { + "propertyName": "Animation" + }, + { + "propertyName": "ButtonFSM" + }, + { + "folderName": "Animation", + "children": [ + { + "propertyName": "IdleAnimationName" + }, + { + "propertyName": "FocusedAnimationName" + }, + { + "propertyName": "PressedAnimationName" + } + ] + } + ] + } + }, + { + "description": "Smoothly change an effect on buttons according to their state.", + "fullName": "Button object effect tween", + "helpPath": "", + "iconUrl": "", + "name": "ButtonObjectEffectTween", + "objectType": "", + "previewIconUrl": "", + "eventsFunctions": [ + { + "fullName": "", + "functionType": "Action", + "name": "onCreated", + "sentence": "", + "events": [ + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [], + "actions": [ + { + "type": { + "value": "SetNumberVariable" + }, + "parameters": [ + "EffectValue", + "=", + "IdleValue" + ] + } + ] + } + ], + "parameters": [ + { + "description": "Object", + "name": "Object", + "type": "object" + }, + { + "description": "Behavior", + "name": "Behavior", + "supplementaryInformation": "ButtonStates::ButtonObjectEffectTween", + "type": "behavior" + } + ], + "objectGroups": [] + }, + { + "fullName": "", + "functionType": "Action", + "name": "doStepPreEvents", + "sentence": "", + "events": [ + { + "colorB": 228, + "colorG": 176, + "colorR": 74, + "creationTime": 0, + "name": "States", + "source": "", + "type": "BuiltinCommonInstructions::Group", + "events": [ + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [ + { + "type": { + "value": "ButtonStates::ButtonFSM::IsIdle" + }, + "parameters": [ + "Object", + "ButtonFSM", + "" + ] + }, + { + "type": { + "value": "StringVariable" + }, + "parameters": [ + "PreviousState", + "!=", + "\"Idle\"" + ] + } + ], + "actions": [ + { + "type": { + "value": "SetStringVariable" + }, + "parameters": [ + "PreviousState", + "=", + "\"Idle\"" + ] + }, + { + "type": { + "value": "ButtonStates::ButtonObjectEffectTween::FadeOut" + }, + "parameters": [ + "Object", + "Behavior", + "IdleValue", + "" + ] + } + ] + }, + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [ + { + "type": { + "value": "ButtonStates::ButtonFSM::IsFocused" + }, + "parameters": [ + "Object", + "ButtonFSM", + "" + ] + } + ], + "actions": [], + "events": [ + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [ + { + "type": { + "value": "StringVariable" + }, + "parameters": [ + "PreviousState", + "=", + "\"Idle\"" + ] + } + ], + "actions": [ + { + "type": { + "value": "SetStringVariable" + }, + "parameters": [ + "PreviousState", + "=", + "\"Focused\"" + ] + }, + { + "type": { + "value": "ButtonStates::ButtonObjectEffectTween::FadeIn" + }, + "parameters": [ + "Object", + "Behavior", + "FocusedValue", + "" + ] + } + ] + }, + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [ + { + "type": { + "value": "StringVariable" + }, + "parameters": [ + "PreviousState", + "=", + "\"Pressed\"" + ] + } + ], + "actions": [ + { + "type": { + "value": "SetStringVariable" + }, + "parameters": [ + "PreviousState", + "=", + "\"Focused\"" + ] + }, + { + "type": { + "value": "ButtonStates::ButtonObjectEffectTween::FadeOut" + }, + "parameters": [ + "Object", + "Behavior", + "FocusedValue", + "" + ] + } + ] + } + ] + }, + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [ + { + "type": { + "value": "ButtonStates::ButtonFSM::IsPressed" + }, + "parameters": [ + "Object", + "ButtonFSM", + "" + ] + }, + { + "type": { + "value": "StringVariable" + }, + "parameters": [ + "PreviousState", + "!=", + "\"Pressed\"" + ] + } + ], + "actions": [ + { + "type": { + "value": "SetStringVariable" + }, + "parameters": [ + "PreviousState", + "=", + "\"Pressed\"" + ] + }, + { + "type": { + "value": "ButtonStates::ButtonObjectEffectTween::FadeIn" + }, + "parameters": [ + "Object", + "Behavior", + "PressedValue", + "" + ] + } + ] + } + ], + "parameters": [] + }, + { + "colorB": 228, + "colorG": 176, + "colorR": 74, + "creationTime": 0, + "name": "Tween", + "source": "", + "type": "BuiltinCommonInstructions::Group", + "events": [ + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [ + { + "type": { + "value": "StringVariable" + }, + "parameters": [ + "TweenState", + "=", + "\"FadeIn\"" + ] + } + ], + "actions": [ + { + "type": { + "value": "ButtonStates::ButtonObjectEffectTween::PlayTween" + }, + "parameters": [ + "Object", + "Behavior", + "FadeInDuration", + "FadeInEasing", + "" + ] + } + ] + }, + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [ + { + "type": { + "value": "StringVariable" + }, + "parameters": [ + "TweenState", + "=", + "\"FadeOut\"" + ] + } + ], + "actions": [ + { + "type": { + "value": "ButtonStates::ButtonObjectEffectTween::PlayTween" + }, + "parameters": [ + "Object", + "Behavior", + "FadeOutDuration", + "FadeOutEasing", + "" + ] + } + ] + } + ], + "parameters": [] + }, + { + "colorB": 228, + "colorG": 176, + "colorR": 74, + "creationTime": 0, + "name": "Toggle effect", + "source": "", + "type": "BuiltinCommonInstructions::Group", + "events": [ + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [ + { + "type": { + "value": "BooleanVariable" + }, + "parameters": [ + "ShouldDisableEffectInIdle", + "True", + "" + ] + } + ], + "actions": [], + "events": [ + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [ + { + "type": { + "value": "ButtonStates::ButtonFSM::IsIdle" + }, + "parameters": [ + "Object", + "ButtonFSM", + "" + ] + }, + { + "type": { + "value": "StringVariable" + }, + "parameters": [ + "TweenState", + "=", + "\"NoTween\"" + ] + } + ], + "actions": [ + { + "type": { + "value": "EffectCapability::EffectBehavior::EnableEffect" + }, + "parameters": [ + "Object", + "Effect", + "EffectName", + "no" + ] + } + ] + }, + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [ + { + "type": { + "inverted": true, + "value": "ButtonStates::ButtonFSM::IsIdle" + }, + "parameters": [ + "Object", + "ButtonFSM", + "" + ] + } + ], + "actions": [ + { + "type": { + "value": "EffectCapability::EffectBehavior::EnableEffect" + }, + "parameters": [ + "Object", + "Effect", + "EffectName", + "yes" + ] + } + ] + } + ] + } + ], + "parameters": [] + } + ], + "parameters": [ + { + "description": "Object", + "name": "Object", + "type": "object" + }, + { + "description": "Behavior", + "name": "Behavior", + "supplementaryInformation": "ButtonStates::ButtonObjectEffectTween", + "type": "behavior" + } + ], + "objectGroups": [] + }, + { + "fullName": "Time delta", + "functionType": "Expression", + "name": "TimeDelta", + "private": true, + "sentence": "", + "events": [ + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [], + "actions": [ + { + "type": { + "value": "SetReturnNumber" + }, + "parameters": [ + "TimeDelta() * LayerTimeScale(Object.Layer())" + ] + } + ] + } + ], + "expressionType": { + "type": "expression" + }, + "parameters": [ + { + "description": "Object", + "name": "Object", + "type": "object" + }, + { + "description": "Behavior", + "name": "Behavior", + "supplementaryInformation": "ButtonStates::ButtonObjectEffectTween", + "type": "behavior" + } + ], + "objectGroups": [] + }, + { + "fullName": "Fade in", + "functionType": "Action", + "name": "FadeIn", + "private": true, + "sentence": "_PARAM0_ fade in to _PARAM2_", + "events": [ + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [], + "actions": [ + { + "type": { + "value": "SetNumberVariable" + }, + "parameters": [ + "TweenTime", + "=", + "0" + ] + }, + { + "type": { + "value": "SetStringVariable" + }, + "parameters": [ + "TweenState", + "=", + "\"FadeIn\"" + ] + }, + { + "type": { + "value": "SetNumberVariable" + }, + "parameters": [ + "TweenInitialValue", + "=", + "EffectValue" + ] + }, + { + "type": { + "value": "SetNumberVariable" + }, + "parameters": [ + "TweenTargetedValue", + "=", + "Value" + ] + } + ] + } + ], + "parameters": [ + { + "description": "Object", + "name": "Object", + "type": "object" + }, + { + "description": "Behavior", + "name": "Behavior", + "supplementaryInformation": "ButtonStates::ButtonObjectEffectTween", + "type": "behavior" + }, + { + "description": "Value", + "name": "Value", + "type": "expression" + } + ], + "objectGroups": [] + }, + { + "fullName": "Fade out", + "functionType": "Action", + "name": "FadeOut", + "private": true, + "sentence": "_PARAM0_ fade out to _PARAM2_", + "events": [ + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [], + "actions": [ + { + "type": { + "value": "SetNumberVariable" + }, + "parameters": [ + "TweenTime", + "=", + "0" + ] + }, + { + "type": { + "value": "SetStringVariable" + }, + "parameters": [ + "TweenState", + "=", + "\"FadeOut\"" + ] + }, + { + "type": { + "value": "SetNumberVariable" + }, + "parameters": [ + "TweenInitialValue", + "=", + "EffectValue" + ] + }, + { + "type": { + "value": "SetNumberVariable" + }, + "parameters": [ + "TweenTargetedValue", + "=", + "Value" + ] + } + ] + } + ], + "parameters": [ + { + "description": "Object", + "name": "Object", + "type": "object" + }, + { + "description": "Behavior", + "name": "Behavior", + "supplementaryInformation": "ButtonStates::ButtonObjectEffectTween", + "type": "behavior" + }, + { + "description": "Value", + "name": "Value", + "type": "expression" + } + ], + "objectGroups": [] + }, + { + "fullName": "Play tween", + "functionType": "Action", + "name": "PlayTween", + "private": true, + "sentence": "Tween the effect property of object _PARAM0_ over _PARAM2_ seconds with _PARAM3_ easing", + "events": [ + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [ + { + "type": { + "value": "NumberVariable" + }, + "parameters": [ + "TweenTime", + "<", + "Duration" + ] + } + ], + "actions": [ + { + "type": { + "value": "SetNumberVariable" + }, + "parameters": [ + "TweenTime", + "+", + "Object.Behavior::TimeDelta()" + ] + }, + { + "type": { + "value": "SetNumberVariable" + }, + "parameters": [ + "EffectValue", + "=", + "Tween::Ease(Easing, TweenInitialValue, TweenTargetedValue, TweenTime / Duration)" + ] + } + ] + }, + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [ + { + "type": { + "value": "NumberVariable" + }, + "parameters": [ + "TweenTime", + ">=", + "Duration" + ] + } + ], + "actions": [ + { + "type": { + "value": "SetStringVariable" + }, + "parameters": [ + "TweenState", + "=", + "\"NoTween\"" + ] + }, + { + "type": { + "value": "SetNumberVariable" + }, + "parameters": [ + "EffectValue", + "=", + "TweenTargetedValue" + ] + } + ] + }, + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [], + "actions": [ + { + "type": { + "value": "EffectCapability::EffectBehavior::SetEffectDoubleParameter" + }, + "parameters": [ + "Object", + "Effect", + "EffectName", + "EffectProperty", + "EffectValue" + ] + } + ] + } + ], + "parameters": [ + { + "description": "Object", + "name": "Object", + "type": "object" + }, + { + "description": "Behavior", + "name": "Behavior", + "supplementaryInformation": "ButtonStates::ButtonObjectEffectTween", + "type": "behavior" + }, + { + "description": "Duration (in seconds)", + "name": "Duration", + "type": "expression" + }, + { + "description": "Easing", + "name": "Easing", + "supplementaryInformation": "[]", + "type": "string" + } + ], + "objectGroups": [] + }, + { + "description": "the effect name of the object.", + "fullName": "Effect name", + "functionType": "ExpressionAndCondition", + "group": "Button effect tween configuration", + "name": "EffectName", + "sentence": "the effect name", + "events": [ + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [], + "actions": [ + { + "type": { + "value": "SetReturnString" + }, + "parameters": [ + "EffectName" + ] + } + ] + } + ], + "expressionType": { + "type": "string" + }, + "parameters": [ + { + "description": "Object", + "name": "Object", + "type": "object" + }, + { + "description": "Behavior", + "name": "Behavior", + "supplementaryInformation": "ButtonStates::ButtonObjectEffectTween", + "type": "behavior" + } + ], + "objectGroups": [] + }, + { + "description": "the effect parameter of the object. The effect parameter names can be found in the effects tab with the \"Show parameter names\" action of the drop down menu.", + "fullName": "Effect parameter", + "functionType": "ExpressionAndCondition", + "group": "Button effect tween configuration", + "name": "EffectProperty", + "sentence": "the effect parameter", + "events": [ + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [], + "actions": [ + { + "type": { + "value": "SetReturnString" + }, + "parameters": [ + "EffectProperty" + ] + } + ] + } + ], + "expressionType": { + "type": "string" + }, + "parameters": [ + { + "description": "Object", + "name": "Object", + "type": "object" + }, + { + "description": "Behavior", + "name": "Behavior", + "supplementaryInformation": "ButtonStates::ButtonObjectEffectTween", + "type": "behavior" + } + ], + "objectGroups": [] + }, + { + "description": "Change the effect parameter of the object. The effect parameter names can be found in the effects tab with the \"Show parameter names\" action of the drop down menu.", + "fullName": "Effect parameter", + "functionType": "Action", + "getterName": "EffectName", + "group": "Button effect tween configuration", + "name": "SetEffectProperty", + "sentence": "Change the tweened effect of _PARAM0_ to _PARAM2_ with parameter _PARAM3_", + "events": [ + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [], + "actions": [ + { + "type": { + "value": "SetStringVariable" + }, + "parameters": [ + "EffectName", + "=", + "NewEffectName" + ] + }, + { + "type": { + "value": "SetStringVariable" + }, + "parameters": [ + "EffectProperty", + "=", + "NewPropertyName" + ] + } + ] + } + ], + "parameters": [ + { + "description": "Object", + "name": "Object", + "type": "object" + }, + { + "description": "Behavior", + "name": "Behavior", + "supplementaryInformation": "ButtonStates::ButtonObjectEffectTween", + "type": "behavior" + }, + { + "description": "Effect name", + "name": "NewEffectName", + "type": "string" + }, + { + "description": "Parameter name", + "name": "NewPropertyName", + "type": "string" + } + ], + "objectGroups": [] + }, + { + "description": "the idle effect parameter value of the object.", + "fullName": "Idle effect parameter value", + "functionType": "ExpressionAndCondition", + "group": "Button effect tween configuration", + "name": "IdleValue", + "sentence": "the idle effect parameter value", + "events": [ + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [], + "actions": [ + { + "type": { + "value": "SetReturnNumber" + }, + "parameters": [ + "IdleValue" + ] + } + ] + } + ], + "expressionType": { + "type": "expression" + }, + "parameters": [ + { + "description": "Object", + "name": "Object", + "type": "object" + }, + { + "description": "Behavior", + "name": "Behavior", + "supplementaryInformation": "ButtonStates::ButtonObjectEffectTween", + "type": "behavior" + } + ], + "objectGroups": [] + }, + { + "fullName": "", + "functionType": "ActionWithOperator", + "getterName": "IdleValue", + "group": "Button effect tween configuration", + "name": "SetIdleValue", + "sentence": "", + "events": [ + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [], + "actions": [ + { + "type": { + "value": "SetNumberVariable" + }, + "parameters": [ + "IdleValue", + "=", + "Value" + ] + } + ] + } + ], + "parameters": [ + { + "description": "Object", + "name": "Object", + "type": "object" + }, + { + "description": "Behavior", + "name": "Behavior", + "supplementaryInformation": "ButtonStates::ButtonObjectEffectTween", + "type": "behavior" + } + ], + "objectGroups": [] + }, + { + "description": "the focused effect parameter value of the object. The state is Focused when the button is hovered or held outside.", + "fullName": "Focused effect parameter value", + "functionType": "ExpressionAndCondition", + "group": "Button effect tween configuration", + "name": "FocusedValue", + "sentence": "the focused effect parameter value", + "events": [ + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [], + "actions": [ + { + "type": { + "value": "SetReturnNumber" + }, + "parameters": [ + "FocusedValue" + ] + } + ] + } + ], + "expressionType": { + "type": "expression" + }, + "parameters": [ + { + "description": "Object", + "name": "Object", + "type": "object" + }, + { + "description": "Behavior", + "name": "Behavior", + "supplementaryInformation": "ButtonStates::ButtonObjectEffectTween", + "type": "behavior" + } + ], + "objectGroups": [] + }, + { + "fullName": "", + "functionType": "ActionWithOperator", + "getterName": "FocusedValue", + "group": "Button effect tween configuration", + "name": "SetFocusedValue", + "sentence": "", + "events": [ + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [], + "actions": [ + { + "type": { + "value": "SetNumberVariable" + }, + "parameters": [ + "FocusedValue", + "=", + "Value" + ] + } + ] + } + ], + "parameters": [ + { + "description": "Object", + "name": "Object", + "type": "object" + }, + { + "description": "Behavior", + "name": "Behavior", + "supplementaryInformation": "ButtonStates::ButtonObjectEffectTween", + "type": "behavior" + } + ], + "objectGroups": [] + }, + { + "description": "the pressed effect parameter value of the object.", + "fullName": "Pressed effect parameter value", + "functionType": "ExpressionAndCondition", + "group": "Button effect tween configuration", + "name": "PressedValue", + "sentence": "the pressed effect parameter value", + "events": [ + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [], + "actions": [ + { + "type": { + "value": "SetReturnNumber" + }, + "parameters": [ + "PressedValue" + ] + } + ] + } + ], + "expressionType": { + "type": "expression" + }, + "parameters": [ + { + "description": "Object", + "name": "Object", + "type": "object" + }, + { + "description": "Behavior", + "name": "Behavior", + "supplementaryInformation": "ButtonStates::ButtonObjectEffectTween", + "type": "behavior" + } + ], + "objectGroups": [] + }, + { + "fullName": "", + "functionType": "ActionWithOperator", + "getterName": "PressedValue", + "group": "Button effect tween configuration", + "name": "SetPressedValue", + "sentence": "", + "events": [ + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [], + "actions": [ + { + "type": { + "value": "SetNumberVariable" + }, + "parameters": [ + "PressedValue", + "=", + "Value" + ] + } + ] + } + ], + "parameters": [ + { + "description": "Object", + "name": "Object", + "type": "object" + }, + { + "description": "Behavior", + "name": "Behavior", + "supplementaryInformation": "ButtonStates::ButtonObjectEffectTween", + "type": "behavior" + } + ], + "objectGroups": [] + }, + { + "description": "the fade-in easing of the object.", + "fullName": "Fade-in easing", + "functionType": "ExpressionAndCondition", + "group": "Button effect tween configuration", + "name": "FadeInEasing", + "sentence": "the fade-in easing", + "events": [ + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [], + "actions": [ + { + "type": { + "value": "SetReturnString" + }, + "parameters": [ + "FadeInEasing" + ] + } + ] + } + ], + "expressionType": { + "supplementaryInformation": "[\"linear\",\"easeInQuad\",\"easeOutQuad\",\"easeInOutQuad\",\"easeInCubic\",\"easeOutCubic\",\"easeInOutCubic\",\"easeInQuart\",\"easeOutQuart\",\"easeInOutQuart\",\"easeInQuint\",\"easeOutQuint\",\"easeInOutQuint\",\"easeInOutSine\",\"easeInExpo\",\"easeOutExpo\",\"easeInOutExpo\",\"easeInCirc\",\"easeOutCirc\",\"easeInOutCirc\",\"easeOutBounce\",\"easeInBack\",\"easeOutBack\",\"easeInOutBack\",\"elastic\",\"swingFromTo\",\"swingFrom\",\"swingTo\",\"bounce\",\"bouncePast\",\"easeFromTo\",\"easeFrom\",\"easeTo\"]", + "type": "stringWithSelector" + }, + "parameters": [ + { + "description": "Object", + "name": "Object", + "type": "object" + }, + { + "description": "Behavior", + "name": "Behavior", + "supplementaryInformation": "ButtonStates::ButtonObjectEffectTween", + "type": "behavior" + } + ], + "objectGroups": [] + }, + { + "fullName": "", + "functionType": "ActionWithOperator", + "getterName": "FadeInEasing", + "group": "Button effect tween configuration", + "name": "SetFadeInEasing", + "sentence": "", + "events": [ + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [], + "actions": [ + { + "type": { + "value": "SetStringVariable" + }, + "parameters": [ + "FadeInEasing", + "=", + "Value" + ] + } + ] + } + ], + "parameters": [ + { + "description": "Object", + "name": "Object", + "type": "object" + }, + { + "description": "Behavior", + "name": "Behavior", + "supplementaryInformation": "ButtonStates::ButtonObjectEffectTween", + "type": "behavior" + } + ], + "objectGroups": [] + }, + { + "description": "the fade-out easing of the object.", + "fullName": "Fade-out easing", + "functionType": "ExpressionAndCondition", + "group": "Button effect tween configuration", + "name": "FadeOutEasing", + "sentence": "the fade-out easing", + "events": [ + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [], + "actions": [ + { + "type": { + "value": "SetReturnString" + }, + "parameters": [ + "FadeOutEasing" + ] + } + ] + } + ], + "expressionType": { + "supplementaryInformation": "[\"linear\",\"easeInQuad\",\"easeOutQuad\",\"easeInOutQuad\",\"easeInCubic\",\"easeOutCubic\",\"easeInOutCubic\",\"easeInQuart\",\"easeOutQuart\",\"easeInOutQuart\",\"easeInQuint\",\"easeOutQuint\",\"easeInOutQuint\",\"easeInOutSine\",\"easeInExpo\",\"easeOutExpo\",\"easeInOutExpo\",\"easeInCirc\",\"easeOutCirc\",\"easeInOutCirc\",\"easeOutBounce\",\"easeInBack\",\"easeOutBack\",\"easeInOutBack\",\"elastic\",\"swingFromTo\",\"swingFrom\",\"swingTo\",\"bounce\",\"bouncePast\",\"easeFromTo\",\"easeFrom\",\"easeTo\"]", + "type": "stringWithSelector" + }, + "parameters": [ + { + "description": "Object", + "name": "Object", + "type": "object" + }, + { + "description": "Behavior", + "name": "Behavior", + "supplementaryInformation": "ButtonStates::ButtonObjectEffectTween", + "type": "behavior" + } + ], + "objectGroups": [] + }, + { + "fullName": "", + "functionType": "ActionWithOperator", + "getterName": "FadeOutEasing", + "group": "Button effect tween configuration", + "name": "SetFadeOutEasing", + "sentence": "", + "events": [ + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [], + "actions": [ + { + "type": { + "value": "SetStringVariable" + }, + "parameters": [ + "FadeOutEasing", + "=", + "Value" + ] + } + ] + } + ], + "parameters": [ + { + "description": "Object", + "name": "Object", + "type": "object" + }, + { + "description": "Behavior", + "name": "Behavior", + "supplementaryInformation": "ButtonStates::ButtonObjectEffectTween", + "type": "behavior" + } + ], + "objectGroups": [] + }, + { + "description": "the fade-in duration of the object.", + "fullName": "Fade-in duration", + "functionType": "ExpressionAndCondition", + "group": "Button effect tween configuration", + "name": "FadeInDuration", + "sentence": "the fade-in duration", + "events": [ + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [], + "actions": [ + { + "type": { + "value": "SetReturnNumber" + }, + "parameters": [ + "FadeInDuration" + ] + } + ] + } + ], + "expressionType": { + "type": "expression" + }, + "parameters": [ + { + "description": "Object", + "name": "Object", + "type": "object" + }, + { + "description": "Behavior", + "name": "Behavior", + "supplementaryInformation": "ButtonStates::ButtonObjectEffectTween", + "type": "behavior" + } + ], + "objectGroups": [] + }, + { + "fullName": "", + "functionType": "ActionWithOperator", + "getterName": "FadeInDuration", + "group": "Button effect tween configuration", + "name": "SetFadeInDuration", + "sentence": "", + "events": [ + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [], + "actions": [ + { + "type": { + "value": "SetNumberVariable" + }, + "parameters": [ + "FadeInDuration", + "=", + "Value" + ] + } + ] + } + ], + "parameters": [ + { + "description": "Object", + "name": "Object", + "type": "object" + }, + { + "description": "Behavior", + "name": "Behavior", + "supplementaryInformation": "ButtonStates::ButtonObjectEffectTween", + "type": "behavior" + } + ], + "objectGroups": [] + }, + { + "description": "the fade-out duration of the object.", + "fullName": "Fade-out duration", + "functionType": "ExpressionAndCondition", + "group": "Button effect tween configuration", + "name": "FadeOutDuration", + "sentence": "the fade-out duration", + "events": [ + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [], + "actions": [ + { + "type": { + "value": "SetReturnNumber" + }, + "parameters": [ + "FadeOutDuration" + ] + } + ] + } + ], + "expressionType": { + "type": "expression" + }, + "parameters": [ + { + "description": "Object", + "name": "Object", + "type": "object" + }, + { + "description": "Behavior", + "name": "Behavior", + "supplementaryInformation": "ButtonStates::ButtonObjectEffectTween", + "type": "behavior" + } + ], + "objectGroups": [] + }, + { + "fullName": "", + "functionType": "ActionWithOperator", + "getterName": "FadeOutDuration", + "group": "Button effect tween configuration", + "name": "SetFadeOutDuration", + "sentence": "", + "events": [ + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [], + "actions": [ + { + "type": { + "value": "SetNumberVariable" + }, + "parameters": [ + "FadeOutDuration", + "=", + "Value" + ] + } + ] + } + ], + "parameters": [ + { + "description": "Object", + "name": "Object", + "type": "object" + }, + { + "description": "Behavior", + "name": "Behavior", + "supplementaryInformation": "ButtonStates::ButtonObjectEffectTween", + "type": "behavior" + } + ], + "objectGroups": [] + } + ], + "eventsFunctionsFolderStructure": { + "folderName": "__ROOT", + "children": [ + { + "functionName": "onCreated" + }, + { + "functionName": "doStepPreEvents" + }, + { + "functionName": "TimeDelta" + }, + { + "functionName": "FadeIn" + }, + { + "functionName": "FadeOut" + }, + { + "functionName": "PlayTween" + }, + { + "folderName": "Button effect tween configuration", + "children": [ + { + "functionName": "EffectName" + }, + { + "functionName": "EffectProperty" + }, + { + "functionName": "SetEffectProperty" + }, + { + "functionName": "IdleValue" + }, + { + "functionName": "SetIdleValue" + }, + { + "functionName": "FocusedValue" + }, + { + "functionName": "SetFocusedValue" + }, + { + "functionName": "PressedValue" + }, + { + "functionName": "SetPressedValue" + }, + { + "functionName": "FadeInEasing" + }, + { + "functionName": "SetFadeInEasing" + }, + { + "functionName": "FadeOutEasing" + }, + { + "functionName": "SetFadeOutEasing" + }, + { + "functionName": "FadeInDuration" + }, + { + "functionName": "SetFadeInDuration" + }, + { + "functionName": "FadeOutDuration" + }, + { + "functionName": "SetFadeOutDuration" + } + ] + } + ] + }, + "propertyDescriptors": [ + { + "value": "", + "type": "Behavior", + "label": "Button states", + "extraInformation": [ + "ButtonStates::ButtonFSM" + ], + "choices": [], + "name": "ButtonFSM" + }, + { + "value": "", + "type": "Behavior", + "label": "Effect capability", + "extraInformation": [ + "EffectCapability::EffectBehavior" + ], + "choices": [], + "name": "Effect" + }, + { + "value": "Effect", + "type": "String", + "label": "Effect name", + "group": "Effect", + "name": "EffectName" + }, + { + "value": "", + "type": "String", + "label": "Effect parameter", + "description": "The effect parameter names can be found in the effects tab with the \"Show parameter names\" action of the drop down menu.", + "group": "Effect", + "name": "EffectProperty" + }, + { + "value": "0", + "type": "Number", + "label": "Idle effect parameter value", + "group": "Value", + "name": "IdleValue" + }, + { + "value": "0", + "type": "Number", + "label": "Focused effect parameter value", + "description": "The state is Focused when the button is hovered or held outside.", + "group": "Value", + "name": "FocusedValue" + }, + { + "value": "0", + "type": "Number", + "label": "Pressed effect parameter value", + "group": "Value", + "name": "PressedValue" + }, + { + "value": "easeInOutQuad", + "type": "Choice", + "label": "Fade-in easing", + "group": "Speed", + "choices": [ + { + "label": "linear", + "value": "linear" + }, + { + "label": "easeInQuad", + "value": "easeInQuad" + }, + { + "label": "easeOutQuad", + "value": "easeOutQuad" + }, + { + "label": "easeInOutQuad", + "value": "easeInOutQuad" + }, + { + "label": "easeInCubic", + "value": "easeInCubic" + }, + { + "label": "easeOutCubic", + "value": "easeOutCubic" + }, + { + "label": "easeInOutCubic", + "value": "easeInOutCubic" + }, + { + "label": "easeInQuart", + "value": "easeInQuart" + }, + { + "label": "easeOutQuart", + "value": "easeOutQuart" + }, + { + "label": "easeInOutQuart", + "value": "easeInOutQuart" + }, + { + "label": "easeInQuint", + "value": "easeInQuint" + }, + { + "label": "easeOutQuint", + "value": "easeOutQuint" + }, + { + "label": "easeInOutQuint", + "value": "easeInOutQuint" + }, + { + "label": "easeInOutSine", + "value": "easeInOutSine" + }, + { + "label": "easeInExpo", + "value": "easeInExpo" + }, + { + "label": "easeOutExpo", + "value": "easeOutExpo" + }, + { + "label": "easeInOutExpo", + "value": "easeInOutExpo" + }, + { + "label": "easeInCirc", + "value": "easeInCirc" + }, + { + "label": "easeOutCirc", + "value": "easeOutCirc" + }, + { + "label": "easeInOutCirc", + "value": "easeInOutCirc" + }, + { + "label": "easeOutBounce", + "value": "easeOutBounce" + }, + { + "label": "easeInBack", + "value": "easeInBack" + }, + { + "label": "easeOutBack", + "value": "easeOutBack" + }, + { + "label": "easeInOutBack", + "value": "easeInOutBack" + }, + { + "label": "elastic", + "value": "elastic" + }, + { + "label": "swingFromTo", + "value": "swingFromTo" + }, + { + "label": "swingFrom", + "value": "swingFrom" + }, + { + "label": "swingTo", + "value": "swingTo" + }, + { + "label": "bounce", + "value": "bounce" + }, + { + "label": "bouncePast", + "value": "bouncePast" + }, + { + "label": "easeFromTo", + "value": "easeFromTo" + }, + { + "label": "easeFrom", + "value": "easeFrom" + }, + { + "label": "easeTo", + "value": "easeTo" + } + ], + "name": "FadeInEasing" + }, + { + "value": "easeInOutQuad", + "type": "Choice", + "label": "Fade-out easing", + "group": "Speed", + "choices": [ + { + "label": "linear", + "value": "linear" + }, + { + "label": "easeInQuad", + "value": "easeInQuad" + }, + { + "label": "easeOutQuad", + "value": "easeOutQuad" + }, + { + "label": "easeInOutQuad", + "value": "easeInOutQuad" + }, + { + "label": "easeInCubic", + "value": "easeInCubic" + }, + { + "label": "easeOutCubic", + "value": "easeOutCubic" + }, + { + "label": "easeInOutCubic", + "value": "easeInOutCubic" + }, + { + "label": "easeInQuart", + "value": "easeInQuart" + }, + { + "label": "easeOutQuart", + "value": "easeOutQuart" + }, + { + "label": "easeInOutQuart", + "value": "easeInOutQuart" + }, + { + "label": "easeInQuint", + "value": "easeInQuint" + }, + { + "label": "easeOutQuint", + "value": "easeOutQuint" + }, + { + "label": "easeInOutQuint", + "value": "easeInOutQuint" + }, + { + "label": "easeInOutSine", + "value": "easeInOutSine" + }, + { + "label": "easeInExpo", + "value": "easeInExpo" + }, + { + "label": "easeOutExpo", + "value": "easeOutExpo" + }, + { + "label": "easeInOutExpo", + "value": "easeInOutExpo" + }, + { + "label": "easeInCirc", + "value": "easeInCirc" + }, + { + "label": "easeOutCirc", + "value": "easeOutCirc" + }, + { + "label": "easeInOutCirc", + "value": "easeInOutCirc" + }, + { + "label": "easeOutBounce", + "value": "easeOutBounce" + }, + { + "label": "easeInBack", + "value": "easeInBack" + }, + { + "label": "easeOutBack", + "value": "easeOutBack" + }, + { + "label": "easeInOutBack", + "value": "easeInOutBack" + }, + { + "label": "elastic", + "value": "elastic" + }, + { + "label": "swingFromTo", + "value": "swingFromTo" + }, + { + "label": "swingFrom", + "value": "swingFrom" + }, + { + "label": "swingTo", + "value": "swingTo" + }, + { + "label": "bounce", + "value": "bounce" + }, + { + "label": "bouncePast", + "value": "bouncePast" + }, + { + "label": "easeFromTo", + "value": "easeFromTo" + }, + { + "label": "easeFrom", + "value": "easeFrom" + }, + { + "label": "easeTo", + "value": "easeTo" + } + ], + "name": "FadeOutEasing" + }, + { + "value": "0.125", + "type": "Number", + "unit": "Second", + "label": "Fade-in duration", + "group": "Speed", + "name": "FadeInDuration" + }, + { + "value": "0.5", + "type": "Number", + "unit": "Second", + "label": "Fade-out duration", + "group": "Speed", + "name": "FadeOutDuration" + }, + { + "value": "", + "type": "Boolean", + "label": "Disable the effect in idle state", + "advanced": true, + "name": "ShouldDisableEffectInIdle" + }, + { + "value": "Idle", + "type": "Choice", + "label": "", + "choices": [ + { + "label": "Idle", + "value": "Idle" + }, + { + "label": "Focused", + "value": "Focused" + }, + { + "label": "Pressed", + "value": "Pressed" + } + ], + "hidden": true, + "name": "PreviousState" + }, + { + "value": "0", + "type": "Number", + "label": "", + "hidden": true, + "name": "TweenInitialValue" + }, + { + "value": "0", + "type": "Number", + "label": "", + "hidden": true, + "name": "TweenTargetedValue" + }, + { + "value": "0", + "type": "Number", + "label": "", + "hidden": true, + "name": "TweenTime" + }, + { + "value": "NoTween", + "type": "Choice", + "label": "", + "choices": [ + { + "label": "NoTween", + "value": "NoTween" + }, + { + "label": "FadeIn", + "value": "FadeIn" + }, + { + "label": "FadeOut", + "value": "FadeOut" + } + ], + "hidden": true, + "name": "TweenState" + }, + { + "value": "0", + "type": "Number", + "label": "", + "hidden": true, + "name": "EffectValue" + } + ], + "propertiesFolderStructure": { + "folderName": "__ROOT", + "children": [ + { + "propertyName": "ButtonFSM" + }, + { + "propertyName": "Effect" + }, + { + "folderName": "Effect", + "children": [ + { + "propertyName": "EffectName" + }, + { + "propertyName": "EffectProperty" + } + ] + }, + { + "folderName": "Value", + "children": [ + { + "propertyName": "IdleValue" + }, + { + "propertyName": "FocusedValue" + }, + { + "propertyName": "PressedValue" + } + ] + }, + { + "folderName": "Speed", + "children": [ + { + "propertyName": "FadeInEasing" + }, + { + "propertyName": "FadeOutEasing" + }, + { + "propertyName": "FadeInDuration" + }, + { + "propertyName": "FadeOutDuration" + } + ] + }, + { + "propertyName": "ShouldDisableEffectInIdle" + }, + { + "propertyName": "PreviousState" + }, + { + "propertyName": "TweenInitialValue" + }, + { + "propertyName": "TweenTargetedValue" + }, + { + "propertyName": "TweenTime" + }, + { + "propertyName": "TweenState" + }, + { + "propertyName": "EffectValue" + } + ] + } + }, + { + "description": "Smoothly resize buttons according to their state.", + "fullName": "Button scale tween", + "helpPath": "", + "iconUrl": "", + "name": "ButtonScaleTween", + "objectType": "", + "previewIconUrl": "", + "eventsFunctions": [ + { + "fullName": "", + "functionType": "Action", + "name": "onCreated", + "sentence": "", + "events": [ + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [], + "actions": [ + { + "type": { + "value": "ScalableCapability::ScalableBehavior::SetValue" + }, + "parameters": [ + "Object", + "Scale", + "=", + "IdleScale" + ] + } + ] + } + ], + "parameters": [ + { + "description": "Object", + "name": "Object", + "type": "object" + }, + { + "description": "Behavior", + "name": "Behavior", + "supplementaryInformation": "ButtonStates::ButtonScaleTween", + "type": "behavior" + } + ], + "objectGroups": [] + }, + { + "fullName": "", + "functionType": "Action", + "name": "doStepPreEvents", + "sentence": "", + "events": [ + { + "colorB": 228, + "colorG": 176, + "colorR": 74, + "creationTime": 0, + "name": "Change based on state", + "source": "", + "type": "BuiltinCommonInstructions::Group", + "events": [ + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [ + { + "type": { + "value": "ButtonStates::ButtonFSM::IsIdle" + }, + "parameters": [ + "Object", + "ButtonFSM", + "" + ] + }, + { + "type": { + "value": "StringVariable" + }, + "parameters": [ + "PreviousState", + "!=", + "\"Idle\"" + ] + } + ], + "actions": [ + { + "type": { + "value": "SetStringVariable" + }, + "parameters": [ + "PreviousState", + "=", + "\"Idle\"" + ] + }, + { + "type": { + "value": "ButtonStates::ButtonScaleTween::FadeOut" + }, + "parameters": [ + "Object", + "Behavior", + "IdleScale", + "" + ] + } + ] + }, + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [ + { + "type": { + "value": "ButtonStates::ButtonFSM::IsFocused" + }, + "parameters": [ + "Object", + "ButtonFSM", + "" + ] + } + ], + "actions": [], + "events": [ + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [ + { + "type": { + "value": "StringVariable" + }, + "parameters": [ + "PreviousState", + "=", + "\"Idle\"" + ] + } + ], + "actions": [ + { + "type": { + "value": "SetStringVariable" + }, + "parameters": [ + "PreviousState", + "=", + "\"Focused\"" + ] + }, + { + "type": { + "value": "ButtonStates::ButtonScaleTween::FadeIn" + }, + "parameters": [ + "Object", + "Behavior", + "FocusedScale", + "" + ] + } + ] + }, + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [ + { + "type": { + "value": "StringVariable" + }, + "parameters": [ + "PreviousState", + "=", + "\"Pressed\"" + ] + } + ], + "actions": [ + { + "type": { + "value": "SetStringVariable" + }, + "parameters": [ + "PreviousState", + "=", + "\"Focused\"" + ] + }, + { + "type": { + "value": "ButtonStates::ButtonScaleTween::FadeOut" + }, + "parameters": [ + "Object", + "Behavior", + "FocusedScale", + "" + ] + } + ] + } + ] + }, + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [ + { + "type": { + "value": "ButtonStates::ButtonFSM::IsPressed" + }, + "parameters": [ + "Object", + "ButtonFSM", + "" + ] + }, + { + "type": { + "value": "StringVariable" + }, + "parameters": [ + "PreviousState", + "!=", + "\"Pressed\"" + ] + } + ], + "actions": [ + { + "type": { + "value": "SetStringVariable" + }, + "parameters": [ + "PreviousState", + "=", + "\"Pressed\"" + ] + }, + { + "type": { + "value": "ButtonStates::ButtonScaleTween::FadeIn" + }, + "parameters": [ + "Object", + "Behavior", + "PressedScale", + "" + ] + } + ] + } + ], + "parameters": [] + } + ], + "parameters": [ + { + "description": "Object", + "name": "Object", + "type": "object" + }, + { + "description": "Behavior", + "name": "Behavior", + "supplementaryInformation": "ButtonStates::ButtonScaleTween", + "type": "behavior" + } + ], + "objectGroups": [] + }, + { + "fullName": "Fade in", + "functionType": "Action", + "name": "FadeIn", + "private": true, + "sentence": "_PARAM0_ fade in to _PARAM2_", + "events": [ + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [], + "actions": [ + { + "type": { + "value": "Tween::AddObjectScaleTween" + }, + "parameters": [ + "Object", + "Tween", + "\"__ButtonScaleTween.Fade\"", + "Value", + "Value", + "FadeInEasing", + "1000 * FadeInDuration", + "no", + "yes" + ] + } + ] + } + ], + "parameters": [ + { + "description": "Object", + "name": "Object", + "type": "object" + }, + { + "description": "Behavior", + "name": "Behavior", + "supplementaryInformation": "ButtonStates::ButtonScaleTween", + "type": "behavior" + }, + { + "description": "Value", + "name": "Value", + "type": "expression" + } + ], + "objectGroups": [] + }, + { + "fullName": "Fade out", + "functionType": "Action", + "name": "FadeOut", + "private": true, + "sentence": "_PARAM0_ fade out to _PARAM2_", + "events": [ + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [], + "actions": [ + { + "type": { + "value": "Tween::AddObjectScaleTween" + }, + "parameters": [ + "Object", + "Tween", + "\"__ButtonScaleTween.Fade\"", + "Value", + "Value", + "FadeOutEasing", + "1000 * FadeOutDuration", + "no", + "yes" + ] + } + ] + } + ], + "parameters": [ + { + "description": "Object", + "name": "Object", + "type": "object" + }, + { + "description": "Behavior", + "name": "Behavior", + "supplementaryInformation": "ButtonStates::ButtonScaleTween", + "type": "behavior" + }, + { + "description": "Value", + "name": "Value", + "type": "expression" + } + ], + "objectGroups": [] + }, + { + "description": "the idle state size scale of the object.", + "fullName": "Idle state size scale", + "functionType": "ExpressionAndCondition", + "group": "Button scale tween configuration", + "name": "IdleScale", + "sentence": "the idle state size scale", + "events": [ + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [], + "actions": [ + { + "type": { + "value": "SetReturnNumber" + }, + "parameters": [ + "IdleScale" + ] + } + ] + } + ], + "expressionType": { + "type": "expression" + }, + "parameters": [ + { + "description": "Object", + "name": "Object", + "type": "object" + }, + { + "description": "Behavior", + "name": "Behavior", + "supplementaryInformation": "ButtonStates::ButtonScaleTween", + "type": "behavior" + } + ], + "objectGroups": [] + }, + { + "fullName": "", + "functionType": "ActionWithOperator", + "getterName": "IdleScale", + "group": "Button scale tween configuration", + "name": "SetIdleScale", + "sentence": "", + "events": [ + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [], + "actions": [ + { + "type": { + "value": "SetNumberVariable" + }, + "parameters": [ + "IdleScale", + "=", + "Value" + ] + } + ] + } + ], + "parameters": [ + { + "description": "Object", + "name": "Object", + "type": "object" + }, + { + "description": "Behavior", + "name": "Behavior", + "supplementaryInformation": "ButtonStates::ButtonScaleTween", + "type": "behavior" + } + ], + "objectGroups": [] + }, + { + "description": "the focused state size scale of the object. The state is Focused when the button is hovered or held outside.", + "fullName": "Focused state size scale", + "functionType": "ExpressionAndCondition", + "group": "Button scale tween configuration", + "name": "FocusedScale", + "sentence": "the focused state size scale", + "events": [ + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [], + "actions": [ + { + "type": { + "value": "SetReturnNumber" + }, + "parameters": [ + "FocusedScale" + ] + } + ] + } + ], + "expressionType": { + "type": "expression" + }, + "parameters": [ + { + "description": "Object", + "name": "Object", + "type": "object" + }, + { + "description": "Behavior", + "name": "Behavior", + "supplementaryInformation": "ButtonStates::ButtonScaleTween", + "type": "behavior" + } + ], + "objectGroups": [] + }, + { + "fullName": "", + "functionType": "ActionWithOperator", + "getterName": "FocusedScale", + "group": "Button scale tween configuration", + "name": "SetFocusedScale", + "sentence": "", + "events": [ + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [], + "actions": [ + { + "type": { + "value": "SetNumberVariable" + }, + "parameters": [ + "FocusedScale", + "=", + "Value" + ] + } + ] + } + ], + "parameters": [ + { + "description": "Object", + "name": "Object", + "type": "object" + }, + { + "description": "Behavior", + "name": "Behavior", + "supplementaryInformation": "ButtonStates::ButtonScaleTween", + "type": "behavior" + } + ], + "objectGroups": [] + }, + { + "description": "the pressed state size scale of the object.", + "fullName": "Pressed state size scale", + "functionType": "ExpressionAndCondition", + "group": "Button scale tween configuration", + "name": "PressedScale", + "sentence": "the pressed state size scale", + "events": [ + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [], + "actions": [ + { + "type": { + "value": "SetReturnNumber" + }, + "parameters": [ + "PressedScale" + ] + } + ] + } + ], + "expressionType": { + "type": "expression" + }, + "parameters": [ + { + "description": "Object", + "name": "Object", + "type": "object" + }, + { + "description": "Behavior", + "name": "Behavior", + "supplementaryInformation": "ButtonStates::ButtonScaleTween", + "type": "behavior" + } + ], + "objectGroups": [] + }, + { + "fullName": "", + "functionType": "ActionWithOperator", + "getterName": "PressedScale", + "group": "Button scale tween configuration", + "name": "SetPressedScale", + "sentence": "", + "events": [ + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [], + "actions": [ + { + "type": { + "value": "SetNumberVariable" + }, + "parameters": [ + "PressedScale", + "=", + "Value" + ] + } + ] + } + ], + "parameters": [ + { + "description": "Object", + "name": "Object", + "type": "object" + }, + { + "description": "Behavior", + "name": "Behavior", + "supplementaryInformation": "ButtonStates::ButtonScaleTween", + "type": "behavior" + } + ], + "objectGroups": [] + }, + { + "description": "the fade-in duration of the object.", + "fullName": "Fade-in duration", + "functionType": "ExpressionAndCondition", + "group": "Button scale tween configuration", + "name": "FadeInDuration", + "sentence": "the fade-in duration", + "events": [ + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [], + "actions": [ + { + "type": { + "value": "SetReturnNumber" + }, + "parameters": [ + "FadeInDuration" + ] + } + ] + } + ], + "expressionType": { + "type": "expression" + }, + "parameters": [ + { + "description": "Object", + "name": "Object", + "type": "object" + }, + { + "description": "Behavior", + "name": "Behavior", + "supplementaryInformation": "ButtonStates::ButtonScaleTween", + "type": "behavior" + } + ], + "objectGroups": [] + }, + { + "fullName": "", + "functionType": "ActionWithOperator", + "getterName": "FadeInDuration", + "group": "Button scale tween configuration", + "name": "SetFadeInDuration", + "sentence": "", + "events": [ + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [], + "actions": [ + { + "type": { + "value": "SetNumberVariable" + }, + "parameters": [ + "FadeInDuration", + "=", + "Value" + ] + } + ] + } + ], + "parameters": [ + { + "description": "Object", + "name": "Object", + "type": "object" + }, + { + "description": "Behavior", + "name": "Behavior", + "supplementaryInformation": "ButtonStates::ButtonScaleTween", + "type": "behavior" + } + ], + "objectGroups": [] + }, + { + "description": "the fade-out duration of the object.", + "fullName": "Fade-out duration", + "functionType": "ExpressionAndCondition", + "group": "Button scale tween configuration", + "name": "FadeOutDuration", + "sentence": "the fade-out duration", + "events": [ + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [], + "actions": [ + { + "type": { + "value": "SetReturnNumber" + }, + "parameters": [ + "FadeOutDuration" + ] + } + ] + } + ], + "expressionType": { + "type": "expression" + }, + "parameters": [ + { + "description": "Object", + "name": "Object", + "type": "object" + }, + { + "description": "Behavior", + "name": "Behavior", + "supplementaryInformation": "ButtonStates::ButtonScaleTween", + "type": "behavior" + } + ], + "objectGroups": [] + }, + { + "fullName": "", + "functionType": "ActionWithOperator", + "getterName": "FadeOutDuration", + "group": "Button scale tween configuration", + "name": "SetFadeOutDuration", + "sentence": "", + "events": [ + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [], + "actions": [ + { + "type": { + "value": "SetNumberVariable" + }, + "parameters": [ + "FadeOutDuration", + "=", + "Value" + ] + } + ] + } + ], + "parameters": [ + { + "description": "Object", + "name": "Object", + "type": "object" + }, + { + "description": "Behavior", + "name": "Behavior", + "supplementaryInformation": "ButtonStates::ButtonScaleTween", + "type": "behavior" + } + ], + "objectGroups": [] + }, + { + "description": "the fade-in easing of the object.", + "fullName": "Fade-in easing", + "functionType": "ExpressionAndCondition", + "group": "Button scale tween configuration", + "name": "FadeInEasing", + "sentence": "the fade-in easing", + "events": [ + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [], + "actions": [ + { + "type": { + "value": "SetReturnString" + }, + "parameters": [ + "FadeInEasing" + ] + } + ] + } + ], + "expressionType": { + "supplementaryInformation": "[\"linear\",\"easeInQuad\",\"easeOutQuad\",\"easeInOutQuad\",\"easeInCubic\",\"easeOutCubic\",\"easeInOutCubic\",\"easeInQuart\",\"easeOutQuart\",\"easeInOutQuart\",\"easeInQuint\",\"easeOutQuint\",\"easeInOutQuint\",\"easeInOutSine\",\"easeInExpo\",\"easeOutExpo\",\"easeInOutExpo\",\"easeInCirc\",\"easeOutCirc\",\"easeInOutCirc\",\"easeOutBounce\",\"easeInBack\",\"easeOutBack\",\"easeInOutBack\",\"elastic\",\"swingFromTo\",\"swingFrom\",\"swingTo\",\"bounce\",\"bouncePast\",\"easeFromTo\",\"easeFrom\",\"easeTo\"]", + "type": "stringWithSelector" + }, + "parameters": [ + { + "description": "Object", + "name": "Object", + "type": "object" + }, + { + "description": "Behavior", + "name": "Behavior", + "supplementaryInformation": "ButtonStates::ButtonScaleTween", + "type": "behavior" + } + ], + "objectGroups": [] + }, + { + "fullName": "", + "functionType": "ActionWithOperator", + "getterName": "FadeInEasing", + "group": "Button scale tween configuration", + "name": "SetFadeInEasing", + "sentence": "", + "events": [ + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [], + "actions": [ + { + "type": { + "value": "SetStringVariable" + }, + "parameters": [ + "FadeInEasing", + "=", + "Value" + ] + } + ] + } + ], + "parameters": [ + { + "description": "Object", + "name": "Object", + "type": "object" + }, + { + "description": "Behavior", + "name": "Behavior", + "supplementaryInformation": "ButtonStates::ButtonScaleTween", + "type": "behavior" + } + ], + "objectGroups": [] + }, + { + "description": "the fade-out easing of the object.", + "fullName": "Fade-out easing", + "functionType": "ExpressionAndCondition", + "group": "Button scale tween configuration", + "name": "FadeOutEasing", + "sentence": "the fade-out easing", + "events": [ + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [], + "actions": [ + { + "type": { + "value": "SetReturnString" + }, + "parameters": [ + "FadeOutEasing" + ] + } + ] + } + ], + "expressionType": { + "supplementaryInformation": "[\"linear\",\"easeInQuad\",\"easeOutQuad\",\"easeInOutQuad\",\"easeInCubic\",\"easeOutCubic\",\"easeInOutCubic\",\"easeInQuart\",\"easeOutQuart\",\"easeInOutQuart\",\"easeInQuint\",\"easeOutQuint\",\"easeInOutQuint\",\"easeInOutSine\",\"easeInExpo\",\"easeOutExpo\",\"easeInOutExpo\",\"easeInCirc\",\"easeOutCirc\",\"easeInOutCirc\",\"easeOutBounce\",\"easeInBack\",\"easeOutBack\",\"easeInOutBack\",\"elastic\",\"swingFromTo\",\"swingFrom\",\"swingTo\",\"bounce\",\"bouncePast\",\"easeFromTo\",\"easeFrom\",\"easeTo\"]", + "type": "stringWithSelector" + }, + "parameters": [ + { + "description": "Object", + "name": "Object", + "type": "object" + }, + { + "description": "Behavior", + "name": "Behavior", + "supplementaryInformation": "ButtonStates::ButtonScaleTween", + "type": "behavior" + } + ], + "objectGroups": [] + }, + { + "fullName": "", + "functionType": "ActionWithOperator", + "getterName": "FadeOutEasing", + "group": "Button scale tween configuration", + "name": "SetFadeOutEasing", + "sentence": "", + "events": [ + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [], + "actions": [ + { + "type": { + "value": "SetStringVariable" + }, + "parameters": [ + "FadeOutEasing", + "=", + "Value" + ] + } + ] + } + ], + "parameters": [ + { + "description": "Object", + "name": "Object", + "type": "object" + }, + { + "description": "Behavior", + "name": "Behavior", + "supplementaryInformation": "ButtonStates::ButtonScaleTween", + "type": "behavior" + } + ], + "objectGroups": [] + } + ], + "eventsFunctionsFolderStructure": { + "folderName": "__ROOT", + "children": [ + { + "functionName": "onCreated" + }, + { + "functionName": "doStepPreEvents" + }, + { + "functionName": "FadeIn" + }, + { + "functionName": "FadeOut" + }, + { + "folderName": "Button scale tween configuration", + "children": [ + { + "functionName": "IdleScale" + }, + { + "functionName": "SetIdleScale" + }, + { + "functionName": "FocusedScale" + }, + { + "functionName": "SetFocusedScale" + }, + { + "functionName": "PressedScale" + }, + { + "functionName": "SetPressedScale" + }, + { + "functionName": "FadeInDuration" + }, + { + "functionName": "SetFadeInDuration" + }, + { + "functionName": "FadeOutDuration" + }, + { + "functionName": "SetFadeOutDuration" + }, + { + "functionName": "FadeInEasing" + }, + { + "functionName": "SetFadeInEasing" + }, + { + "functionName": "FadeOutEasing" + }, + { + "functionName": "SetFadeOutEasing" + } + ] + } + ] + }, + "propertyDescriptors": [ + { + "value": "", + "type": "Behavior", + "label": "Scalable capability", + "extraInformation": [ + "ScalableCapability::ScalableBehavior" + ], + "choices": [], + "name": "Scale" + }, + { + "value": "", + "type": "Behavior", + "label": "Button states behavior (required)", + "extraInformation": [ + "ButtonStates::ButtonFSM" + ], + "choices": [], + "name": "ButtonFSM" + }, + { + "value": "", + "type": "Behavior", + "label": "Tween behavior (required)", + "extraInformation": [ + "Tween::TweenBehavior" + ], + "choices": [], + "name": "Tween" + }, + { + "value": "1", + "type": "Number", + "unit": "Dimensionless", + "label": "Idle state size scale", + "group": "Size", + "name": "IdleScale" + }, + { + "value": "1", + "type": "Number", + "unit": "Dimensionless", + "label": "Focused state size scale", + "description": "The state is Focused when the button is hovered or held outside.", + "group": "Size", + "name": "FocusedScale" + }, + { + "value": "0.1", + "type": "Number", + "unit": "Second", + "label": "Fade-in duration", + "group": "Speed", + "name": "FadeInDuration" + }, + { + "value": "0.2", + "type": "Number", + "unit": "Second", + "label": "Fade-out duration", + "group": "Speed", + "name": "FadeOutDuration" + }, + { + "value": "0.95", + "type": "Number", + "unit": "Dimensionless", + "label": "Pressed state size scale", + "group": "Size", + "name": "PressedScale" + }, + { + "value": "easeInOutQuad", + "type": "Choice", + "label": "Fade-in easing", + "group": "Speed", + "choices": [ + { + "label": "linear", + "value": "linear" + }, + { + "label": "easeInQuad", + "value": "easeInQuad" + }, + { + "label": "easeOutQuad", + "value": "easeOutQuad" + }, + { + "label": "easeInOutQuad", + "value": "easeInOutQuad" + }, + { + "label": "easeInCubic", + "value": "easeInCubic" + }, + { + "label": "easeOutCubic", + "value": "easeOutCubic" + }, + { + "label": "easeInOutCubic", + "value": "easeInOutCubic" + }, + { + "label": "easeInQuart", + "value": "easeInQuart" + }, + { + "label": "easeOutQuart", + "value": "easeOutQuart" + }, + { + "label": "easeInOutQuart", + "value": "easeInOutQuart" + }, + { + "label": "easeInQuint", + "value": "easeInQuint" + }, + { + "label": "easeOutQuint", + "value": "easeOutQuint" + }, + { + "label": "easeInOutQuint", + "value": "easeInOutQuint" + }, + { + "label": "easeInOutSine", + "value": "easeInOutSine" + }, + { + "label": "easeInExpo", + "value": "easeInExpo" + }, + { + "label": "easeOutExpo", + "value": "easeOutExpo" + }, + { + "label": "easeInOutExpo", + "value": "easeInOutExpo" + }, + { + "label": "easeInCirc", + "value": "easeInCirc" + }, + { + "label": "easeOutCirc", + "value": "easeOutCirc" + }, + { + "label": "easeInOutCirc", + "value": "easeInOutCirc" + }, + { + "label": "easeOutBounce", + "value": "easeOutBounce" + }, + { + "label": "easeInBack", + "value": "easeInBack" + }, + { + "label": "easeOutBack", + "value": "easeOutBack" + }, + { + "label": "easeInOutBack", + "value": "easeInOutBack" + }, + { + "label": "elastic", + "value": "elastic" + }, + { + "label": "swingFromTo", + "value": "swingFromTo" + }, + { + "label": "swingFrom", + "value": "swingFrom" + }, + { + "label": "swingTo", + "value": "swingTo" + }, + { + "label": "bounce", + "value": "bounce" + }, + { + "label": "bouncePast", + "value": "bouncePast" + }, + { + "label": "easeFromTo", + "value": "easeFromTo" + }, + { + "label": "easeFrom", + "value": "easeFrom" + }, + { + "label": "easeTo", + "value": "easeTo" + } + ], + "name": "FadeInEasing" + }, + { + "value": "easeInOutQuad", + "type": "Choice", + "label": "Fade-out easing", + "group": "Speed", + "choices": [ + { + "label": "linear", + "value": "linear" + }, + { + "label": "easeInQuad", + "value": "easeInQuad" + }, + { + "label": "easeOutQuad", + "value": "easeOutQuad" + }, + { + "label": "easeInOutQuad", + "value": "easeInOutQuad" + }, + { + "label": "easeInCubic", + "value": "easeInCubic" + }, + { + "label": "easeOutCubic", + "value": "easeOutCubic" + }, + { + "label": "easeInOutCubic", + "value": "easeInOutCubic" + }, + { + "label": "easeInQuart", + "value": "easeInQuart" + }, + { + "label": "easeOutQuart", + "value": "easeOutQuart" + }, + { + "label": "easeInOutQuart", + "value": "easeInOutQuart" + }, + { + "label": "easeInQuint", + "value": "easeInQuint" + }, + { + "label": "easeOutQuint", + "value": "easeOutQuint" + }, + { + "label": "easeInOutQuint", + "value": "easeInOutQuint" + }, + { + "label": "easeInOutSine", + "value": "easeInOutSine" + }, + { + "label": "easeInExpo", + "value": "easeInExpo" + }, + { + "label": "easeOutExpo", + "value": "easeOutExpo" + }, + { + "label": "easeInOutExpo", + "value": "easeInOutExpo" + }, + { + "label": "easeInCirc", + "value": "easeInCirc" + }, + { + "label": "easeOutCirc", + "value": "easeOutCirc" + }, + { + "label": "easeInOutCirc", + "value": "easeInOutCirc" + }, + { + "label": "easeOutBounce", + "value": "easeOutBounce" + }, + { + "label": "easeInBack", + "value": "easeInBack" + }, + { + "label": "easeOutBack", + "value": "easeOutBack" + }, + { + "label": "easeInOutBack", + "value": "easeInOutBack" + }, + { + "label": "elastic", + "value": "elastic" + }, + { + "label": "swingFromTo", + "value": "swingFromTo" + }, + { + "label": "swingFrom", + "value": "swingFrom" + }, + { + "label": "swingTo", + "value": "swingTo" + }, + { + "label": "bounce", + "value": "bounce" + }, + { + "label": "bouncePast", + "value": "bouncePast" + }, + { + "label": "easeFromTo", + "value": "easeFromTo" + }, + { + "label": "easeFrom", + "value": "easeFrom" + }, + { + "label": "easeTo", + "value": "easeTo" + } + ], + "name": "FadeOutEasing" + }, + { + "value": "Idle", + "type": "Choice", + "label": "", + "choices": [ + { + "label": "Idle", + "value": "Idle" + }, + { + "label": "Focused", + "value": "Focused" + }, + { + "label": "Pressed", + "value": "Pressed" + } + ], + "hidden": true, + "name": "PreviousState" + } + ], + "propertiesFolderStructure": { + "folderName": "__ROOT", + "children": [ + { + "propertyName": "Scale" + }, + { + "propertyName": "ButtonFSM" + }, + { + "propertyName": "Tween" + }, + { + "folderName": "Size", + "children": [ + { + "propertyName": "IdleScale" + }, + { + "propertyName": "FocusedScale" + }, + { + "propertyName": "PressedScale" + } + ] + }, + { + "folderName": "Speed", + "children": [ + { + "propertyName": "FadeInDuration" + }, + { + "propertyName": "FadeOutDuration" + }, + { + "propertyName": "FadeInEasing" + }, + { + "propertyName": "FadeOutEasing" + } + ] + }, + { + "propertyName": "PreviousState" + } + ] + } + }, + { + "description": "Smoothly change the color tint of buttons according to their state.", + "fullName": "Button color tint tween", + "helpPath": "", + "iconUrl": "", + "name": "ButtonColorTintTween", + "objectType": "Sprite", + "previewIconUrl": "", + "eventsFunctions": [ + { + "fullName": "", + "functionType": "Action", + "name": "onCreated", + "sentence": "", + "events": [ + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [], + "actions": [ + { + "type": { + "value": "ChangeColor" + }, + "parameters": [ + "Object", + "IdleColorTint" + ] + } + ] + } + ], + "parameters": [ + { + "description": "Object", + "name": "Object", + "supplementaryInformation": "Sprite", + "type": "object" + }, + { + "description": "Behavior", + "name": "Behavior", + "supplementaryInformation": "ButtonStates::ButtonColorTintTween", + "type": "behavior" + } + ], + "objectGroups": [] + }, + { + "fullName": "", + "functionType": "Action", + "name": "doStepPreEvents", + "sentence": "", + "events": [ + { + "colorB": 228, + "colorG": 176, + "colorR": 74, + "creationTime": 0, + "name": "States", + "source": "", + "type": "BuiltinCommonInstructions::Group", + "events": [ + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [ + { + "type": { + "value": "ButtonStates::ButtonFSM::IsIdle" + }, + "parameters": [ + "Object", + "ButtonFSM", + "" + ] + }, + { + "type": { + "value": "StringVariable" + }, + "parameters": [ + "PreviousState", + "!=", + "\"Idle\"" + ] + } + ], + "actions": [ + { + "type": { + "value": "SetStringVariable" + }, + "parameters": [ + "PreviousState", + "=", + "\"Idle\"" + ] + }, + { + "type": { + "value": "ButtonStates::ButtonColorTintTween::FadeOut" + }, + "parameters": [ + "Object", + "Behavior", + "IdleColorTint", + "" + ] + } + ] + }, + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [ + { + "type": { + "value": "ButtonStates::ButtonFSM::IsFocused" + }, + "parameters": [ + "Object", + "ButtonFSM", + "" + ] + } + ], + "actions": [], + "events": [ + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [ + { + "type": { + "value": "StringVariable" + }, + "parameters": [ + "PreviousState", + "=", + "\"Idle\"" + ] + } + ], + "actions": [ + { + "type": { + "value": "SetStringVariable" + }, + "parameters": [ + "PreviousState", + "=", + "\"Focused\"" + ] + }, + { + "type": { + "value": "ButtonStates::ButtonColorTintTween::FadeIn" + }, + "parameters": [ + "Object", + "Behavior", + "FocusedColorTint", + "" + ] + } + ] + }, + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [ + { + "type": { + "value": "StringVariable" + }, + "parameters": [ + "PreviousState", + "=", + "\"Pressed\"" + ] + } + ], + "actions": [ + { + "type": { + "value": "SetStringVariable" + }, + "parameters": [ + "PreviousState", + "=", + "\"Focused\"" + ] + }, + { + "type": { + "value": "ButtonStates::ButtonColorTintTween::FadeOut" + }, + "parameters": [ + "Object", + "Behavior", + "FocusedColorTint", + "" + ] + } + ] + } + ] + }, + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [ + { + "type": { + "value": "ButtonStates::ButtonFSM::IsPressed" + }, + "parameters": [ + "Object", + "ButtonFSM", + "" + ] + }, + { + "type": { + "value": "StringVariable" + }, + "parameters": [ + "PreviousState", + "!=", + "\"Pressed\"" + ] + } + ], + "actions": [ + { + "type": { + "value": "SetStringVariable" + }, + "parameters": [ + "PreviousState", + "=", + "\"Pressed\"" + ] + }, + { + "type": { + "value": "ButtonStates::ButtonColorTintTween::FadeIn" + }, + "parameters": [ + "Object", + "Behavior", + "PressedColorTint", + "" + ] + } + ] + } + ], + "parameters": [] + } + ], + "parameters": [ + { + "description": "Object", + "name": "Object", + "supplementaryInformation": "Sprite", + "type": "object" + }, + { + "description": "Behavior", + "name": "Behavior", + "supplementaryInformation": "ButtonStates::ButtonColorTintTween", + "type": "behavior" + } + ], + "objectGroups": [] + }, + { + "fullName": "Fade in", + "functionType": "Action", + "name": "FadeIn", + "private": true, + "sentence": "_PARAM0_ fade in to _PARAM2_", + "events": [ + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [], + "actions": [ + { + "type": { + "value": "Tween::AddObjectColorTween" + }, + "parameters": [ + "Object", + "Tween", + "\"__ButtonColorTintTween.Fade\"", + "Value", + "FadeInEasing", + "1000 * FadeInDuration", + "no", + "yes" + ] + } + ] + } + ], + "parameters": [ + { + "description": "Object", + "name": "Object", + "supplementaryInformation": "Sprite", + "type": "object" + }, + { + "description": "Behavior", + "name": "Behavior", + "supplementaryInformation": "ButtonStates::ButtonColorTintTween", + "type": "behavior" + }, + { + "description": "Value", + "name": "Value", + "type": "color" + } + ], + "objectGroups": [] + }, + { + "fullName": "Fade out", + "functionType": "Action", + "name": "FadeOut", + "private": true, + "sentence": "_PARAM0_ fade out to _PARAM2_", + "events": [ + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [], + "actions": [ + { + "type": { + "value": "Tween::AddObjectColorTween" + }, + "parameters": [ + "Object", + "Tween", + "\"__ButtonColorTintTween.Fade\"", + "Value", + "FadeOutEasing", + "1000 * FadeOutDuration", + "no", + "yes" + ] + } + ] + } + ], + "parameters": [ + { + "description": "Object", + "name": "Object", + "supplementaryInformation": "Sprite", + "type": "object" + }, + { + "description": "Behavior", + "name": "Behavior", + "supplementaryInformation": "ButtonStates::ButtonColorTintTween", + "type": "behavior" + }, + { + "description": "Value", + "name": "Value", + "type": "color" + } + ], + "objectGroups": [] + }, + { + "description": "the idle state color tint of the object.", + "fullName": "Idle state color tint", + "functionType": "ExpressionAndCondition", + "group": "Button color tint tween", + "name": "IdleColorTint", + "sentence": "the idle state color tint", + "events": [ + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [], + "actions": [ + { + "type": { + "value": "SetReturnString" + }, + "parameters": [ + "IdleColorTint" + ] + } + ] + } + ], + "expressionType": { + "type": "color" + }, + "parameters": [ + { + "description": "Object", + "name": "Object", + "supplementaryInformation": "Sprite", + "type": "object" + }, + { + "description": "Behavior", + "name": "Behavior", + "supplementaryInformation": "ButtonStates::ButtonColorTintTween", + "type": "behavior" + } + ], + "objectGroups": [] + }, + { + "fullName": "", + "functionType": "ActionWithOperator", + "getterName": "IdleColorTint", + "group": "Button color tint tween", + "name": "SetIdleColorTint", + "sentence": "", + "events": [ + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [], + "actions": [ + { + "type": { + "value": "SetStringVariable" + }, + "parameters": [ + "IdleColorTint", + "=", + "Value" + ] + } + ] + } + ], + "parameters": [ + { + "description": "Object", + "name": "Object", + "supplementaryInformation": "Sprite", + "type": "object" + }, + { + "description": "Behavior", + "name": "Behavior", + "supplementaryInformation": "ButtonStates::ButtonColorTintTween", + "type": "behavior" + } + ], + "objectGroups": [] + }, + { + "description": "the focused state color tint of the object. The state is Focused when the button is hovered or held outside.", + "fullName": "Focused state color tint", + "functionType": "ExpressionAndCondition", + "group": "Button color tint tween", + "name": "FocusedColorTint", + "sentence": "the focused state color tint", + "events": [ + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [], + "actions": [ + { + "type": { + "value": "SetReturnString" + }, + "parameters": [ + "FocusedColorTint" + ] + } + ] + } + ], + "expressionType": { + "type": "color" + }, + "parameters": [ + { + "description": "Object", + "name": "Object", + "supplementaryInformation": "Sprite", + "type": "object" + }, + { + "description": "Behavior", + "name": "Behavior", + "supplementaryInformation": "ButtonStates::ButtonColorTintTween", + "type": "behavior" + } + ], + "objectGroups": [] + }, + { + "fullName": "", + "functionType": "ActionWithOperator", + "getterName": "FocusedColorTint", + "group": "Button color tint tween", + "name": "SetFocusedColorTint", + "sentence": "", + "events": [ + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [], + "actions": [ + { + "type": { + "value": "SetStringVariable" + }, + "parameters": [ + "FocusedColorTint", + "=", + "Value" + ] + } + ] + } + ], + "parameters": [ + { + "description": "Object", + "name": "Object", + "supplementaryInformation": "Sprite", + "type": "object" + }, + { + "description": "Behavior", + "name": "Behavior", + "supplementaryInformation": "ButtonStates::ButtonColorTintTween", + "type": "behavior" + } + ], + "objectGroups": [] + }, + { + "description": "the pressed state color tint of the object.", + "fullName": "Pressed state color tint", + "functionType": "ExpressionAndCondition", + "group": "Button color tint tween", + "name": "PressedColorTint", + "sentence": "the pressed state color tint", + "events": [ + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [], + "actions": [ + { + "type": { + "value": "SetReturnString" + }, + "parameters": [ + "PressedColorTint" + ] + } + ] + } + ], + "expressionType": { + "type": "color" + }, + "parameters": [ + { + "description": "Object", + "name": "Object", + "supplementaryInformation": "Sprite", + "type": "object" + }, + { + "description": "Behavior", + "name": "Behavior", + "supplementaryInformation": "ButtonStates::ButtonColorTintTween", + "type": "behavior" + } + ], + "objectGroups": [] + }, + { + "fullName": "", + "functionType": "ActionWithOperator", + "getterName": "PressedColorTint", + "group": "Button color tint tween", + "name": "SetPressedColorTint", + "sentence": "", + "events": [ + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [], + "actions": [ + { + "type": { + "value": "SetStringVariable" + }, + "parameters": [ + "PressedColorTint", + "=", + "Value" + ] + } + ] + } + ], + "parameters": [ + { + "description": "Object", + "name": "Object", + "supplementaryInformation": "Sprite", + "type": "object" + }, + { + "description": "Behavior", + "name": "Behavior", + "supplementaryInformation": "ButtonStates::ButtonColorTintTween", + "type": "behavior" + } + ], + "objectGroups": [] + }, + { + "description": "the fade-in duration of the object.", + "fullName": "Fade-in duration", + "functionType": "ExpressionAndCondition", + "group": "Button color tint tween", + "name": "FadeInDuration", + "sentence": "the fade-in duration", + "events": [ + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [], + "actions": [ + { + "type": { + "value": "SetReturnNumber" + }, + "parameters": [ + "FadeInDuration" + ] + } + ] + } + ], + "expressionType": { + "type": "expression" + }, + "parameters": [ + { + "description": "Object", + "name": "Object", + "supplementaryInformation": "Sprite", + "type": "object" + }, + { + "description": "Behavior", + "name": "Behavior", + "supplementaryInformation": "ButtonStates::ButtonColorTintTween", + "type": "behavior" + } + ], + "objectGroups": [] + }, + { + "fullName": "", + "functionType": "ActionWithOperator", + "getterName": "FadeInDuration", + "group": "Button color tint tween", + "name": "SetFadeInDuration", + "sentence": "", + "events": [ + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [], + "actions": [ + { + "type": { + "value": "SetNumberVariable" + }, + "parameters": [ + "FadeInDuration", + "=", + "Value" + ] + } + ] + } + ], + "parameters": [ + { + "description": "Object", + "name": "Object", + "supplementaryInformation": "Sprite", + "type": "object" + }, + { + "description": "Behavior", + "name": "Behavior", + "supplementaryInformation": "ButtonStates::ButtonColorTintTween", + "type": "behavior" + } + ], + "objectGroups": [] + }, + { + "description": "the fade-out duration of the object.", + "fullName": "Fade-out duration", + "functionType": "ExpressionAndCondition", + "group": "Button color tint tween", + "name": "FadeOutDuration", + "sentence": "the fade-out duration", + "events": [ + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [], + "actions": [ + { + "type": { + "value": "SetReturnNumber" + }, + "parameters": [ + "FadeOutDuration" + ] + } + ] + } + ], + "expressionType": { + "type": "expression" + }, + "parameters": [ + { + "description": "Object", + "name": "Object", + "supplementaryInformation": "Sprite", + "type": "object" + }, + { + "description": "Behavior", + "name": "Behavior", + "supplementaryInformation": "ButtonStates::ButtonColorTintTween", + "type": "behavior" + } + ], + "objectGroups": [] + }, + { + "fullName": "", + "functionType": "ActionWithOperator", + "getterName": "FadeOutDuration", + "group": "Button color tint tween", + "name": "SetFadeOutDuration", + "sentence": "", + "events": [ + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [], + "actions": [ + { + "type": { + "value": "SetNumberVariable" + }, + "parameters": [ + "FadeOutDuration", + "=", + "Value" + ] + } + ] + } + ], + "parameters": [ + { + "description": "Object", + "name": "Object", + "supplementaryInformation": "Sprite", + "type": "object" + }, + { + "description": "Behavior", + "name": "Behavior", + "supplementaryInformation": "ButtonStates::ButtonColorTintTween", + "type": "behavior" + } + ], + "objectGroups": [] + }, + { + "description": "the fade-in easing of the object.", + "fullName": "Fade-in easing", + "functionType": "ExpressionAndCondition", + "group": "Button color tint tween", + "name": "FadeInEasing", + "sentence": "the fade-in easing", + "events": [ + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [], + "actions": [ + { + "type": { + "value": "SetReturnString" + }, + "parameters": [ + "FadeInEasing" + ] + } + ] + } + ], + "expressionType": { + "supplementaryInformation": "[\"linear\",\"easeInQuad\",\"easeOutQuad\",\"easeInOutQuad\",\"easeInCubic\",\"easeOutCubic\",\"easeInOutCubic\",\"easeInQuart\",\"easeOutQuart\",\"easeInOutQuart\",\"easeInQuint\",\"easeOutQuint\",\"easeInOutQuint\",\"easeInOutSine\",\"easeInExpo\",\"easeOutExpo\",\"easeInOutExpo\",\"easeInCirc\",\"easeOutCirc\",\"easeInOutCirc\",\"easeOutBounce\",\"easeInBack\",\"easeOutBack\",\"easeInOutBack\",\"elastic\",\"swingFromTo\",\"swingFrom\",\"swingTo\",\"bounce\",\"bouncePast\",\"easeFromTo\",\"easeFrom\",\"easeTo\"]", + "type": "stringWithSelector" + }, + "parameters": [ + { + "description": "Object", + "name": "Object", + "supplementaryInformation": "Sprite", + "type": "object" + }, + { + "description": "Behavior", + "name": "Behavior", + "supplementaryInformation": "ButtonStates::ButtonColorTintTween", + "type": "behavior" + } + ], + "objectGroups": [] + }, + { + "fullName": "", + "functionType": "ActionWithOperator", + "getterName": "FadeInEasing", + "group": "Button color tint tween", + "name": "SetFadeInEasing", + "sentence": "", + "events": [ + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [], + "actions": [ + { + "type": { + "value": "SetStringVariable" + }, + "parameters": [ + "FadeInEasing", + "=", + "Value" + ] + } + ] + } + ], + "parameters": [ + { + "description": "Object", + "name": "Object", + "supplementaryInformation": "Sprite", + "type": "object" + }, + { + "description": "Behavior", + "name": "Behavior", + "supplementaryInformation": "ButtonStates::ButtonColorTintTween", + "type": "behavior" + } + ], + "objectGroups": [] + }, + { + "description": "the fade-out easing of the object.", + "fullName": "Fade-out easing", + "functionType": "ExpressionAndCondition", + "group": "Button color tint tween", + "name": "FadeOutEasing", + "sentence": "the fade-out easing", + "events": [ + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [], + "actions": [ + { + "type": { + "value": "SetReturnString" + }, + "parameters": [ + "FadeOutEasing" + ] + } + ] + } + ], + "expressionType": { + "supplementaryInformation": "[\"linear\",\"easeInQuad\",\"easeOutQuad\",\"easeInOutQuad\",\"easeInCubic\",\"easeOutCubic\",\"easeInOutCubic\",\"easeInQuart\",\"easeOutQuart\",\"easeInOutQuart\",\"easeInQuint\",\"easeOutQuint\",\"easeInOutQuint\",\"easeInOutSine\",\"easeInExpo\",\"easeOutExpo\",\"easeInOutExpo\",\"easeInCirc\",\"easeOutCirc\",\"easeInOutCirc\",\"easeOutBounce\",\"easeInBack\",\"easeOutBack\",\"easeInOutBack\",\"elastic\",\"swingFromTo\",\"swingFrom\",\"swingTo\",\"bounce\",\"bouncePast\",\"easeFromTo\",\"easeFrom\",\"easeTo\"]", + "type": "stringWithSelector" + }, + "parameters": [ + { + "description": "Object", + "name": "Object", + "supplementaryInformation": "Sprite", + "type": "object" + }, + { + "description": "Behavior", + "name": "Behavior", + "supplementaryInformation": "ButtonStates::ButtonColorTintTween", + "type": "behavior" + } + ], + "objectGroups": [] + }, + { + "fullName": "", + "functionType": "ActionWithOperator", + "getterName": "FadeOutEasing", + "group": "Button color tint tween", + "name": "SetFadeOutEasing", + "sentence": "", + "events": [ + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [], + "actions": [ + { + "type": { + "value": "SetStringVariable" + }, + "parameters": [ + "FadeOutEasing", + "=", + "Value" + ] + } + ] + } + ], + "parameters": [ + { + "description": "Object", + "name": "Object", + "supplementaryInformation": "Sprite", + "type": "object" + }, + { + "description": "Behavior", + "name": "Behavior", + "supplementaryInformation": "ButtonStates::ButtonColorTintTween", + "type": "behavior" + } + ], + "objectGroups": [] + } + ], + "eventsFunctionsFolderStructure": { + "folderName": "__ROOT", + "children": [ + { + "functionName": "onCreated" + }, + { + "functionName": "doStepPreEvents" + }, + { + "functionName": "FadeIn" + }, + { + "functionName": "FadeOut" + }, + { + "folderName": "Button color tint tween", + "children": [ + { + "functionName": "IdleColorTint" + }, + { + "functionName": "SetIdleColorTint" + }, + { + "functionName": "FocusedColorTint" + }, + { + "functionName": "SetFocusedColorTint" + }, + { + "functionName": "PressedColorTint" + }, + { + "functionName": "SetPressedColorTint" + }, + { + "functionName": "FadeInDuration" + }, + { + "functionName": "SetFadeInDuration" + }, + { + "functionName": "FadeOutDuration" + }, + { + "functionName": "SetFadeOutDuration" + }, + { + "functionName": "FadeInEasing" + }, + { + "functionName": "SetFadeInEasing" + }, + { + "functionName": "FadeOutEasing" + }, + { + "functionName": "SetFadeOutEasing" + } + ] + } + ] + }, + "propertyDescriptors": [ + { + "value": "", + "type": "Behavior", + "label": "Button states", + "extraInformation": [ + "ButtonStates::ButtonFSM" + ], + "choices": [], + "name": "ButtonFSM" + }, + { + "value": "", + "type": "Behavior", + "label": "Tween", + "extraInformation": [ + "Tween::TweenBehavior" + ], + "choices": [], + "name": "Tween" + }, + { + "value": "255;255;255", + "type": "Color", + "label": "Idle state color tint", + "group": "Color", + "name": "IdleColorTint" + }, + { + "value": "192;192;192", + "type": "Color", + "label": "Focused state color tint", + "description": "The state is Focused when the button is hovered or held outside.", + "group": "Color", + "name": "FocusedColorTint" + }, + { + "value": "64;64;64", + "type": "Color", + "label": "Pressed state color tint", + "group": "Color", + "name": "PressedColorTint" + }, + { + "value": "0.1", + "type": "Number", + "unit": "Second", + "label": "Fade-in duration", + "group": "Speed", + "name": "FadeInDuration" + }, + { + "value": "0.2", + "type": "Number", + "unit": "Second", + "label": "Fade-out duration", + "group": "Speed", + "name": "FadeOutDuration" + }, + { + "value": "easeInOutQuad", + "type": "Choice", + "label": "Fade-in easing", + "group": "Speed", + "choices": [ + { + "label": "linear", + "value": "linear" + }, + { + "label": "easeInQuad", + "value": "easeInQuad" + }, + { + "label": "easeOutQuad", + "value": "easeOutQuad" + }, + { + "label": "easeInOutQuad", + "value": "easeInOutQuad" + }, + { + "label": "easeInCubic", + "value": "easeInCubic" + }, + { + "label": "easeOutCubic", + "value": "easeOutCubic" + }, + { + "label": "easeInOutCubic", + "value": "easeInOutCubic" + }, + { + "label": "easeInQuart", + "value": "easeInQuart" + }, + { + "label": "easeOutQuart", + "value": "easeOutQuart" + }, + { + "label": "easeInOutQuart", + "value": "easeInOutQuart" + }, + { + "label": "easeInQuint", + "value": "easeInQuint" + }, + { + "label": "easeOutQuint", + "value": "easeOutQuint" + }, + { + "label": "easeInOutQuint", + "value": "easeInOutQuint" + }, + { + "label": "easeInOutSine", + "value": "easeInOutSine" + }, + { + "label": "easeInExpo", + "value": "easeInExpo" + }, + { + "label": "easeOutExpo", + "value": "easeOutExpo" + }, + { + "label": "easeInOutExpo", + "value": "easeInOutExpo" + }, + { + "label": "easeInCirc", + "value": "easeInCirc" + }, + { + "label": "easeOutCirc", + "value": "easeOutCirc" + }, + { + "label": "easeInOutCirc", + "value": "easeInOutCirc" + }, + { + "label": "easeOutBounce", + "value": "easeOutBounce" + }, + { + "label": "easeInBack", + "value": "easeInBack" + }, + { + "label": "easeOutBack", + "value": "easeOutBack" + }, + { + "label": "easeInOutBack", + "value": "easeInOutBack" + }, + { + "label": "elastic", + "value": "elastic" + }, + { + "label": "swingFromTo", + "value": "swingFromTo" + }, + { + "label": "swingFrom", + "value": "swingFrom" + }, + { + "label": "swingTo", + "value": "swingTo" + }, + { + "label": "bounce", + "value": "bounce" + }, + { + "label": "bouncePast", + "value": "bouncePast" + }, + { + "label": "easeFromTo", + "value": "easeFromTo" + }, + { + "label": "easeFrom", + "value": "easeFrom" + }, + { + "label": "easeTo", + "value": "easeTo" + } + ], + "name": "FadeInEasing" + }, + { + "value": "easeInOutQuad", + "type": "Choice", + "label": "Fade-out easing", + "group": "Speed", + "choices": [ + { + "label": "linear", + "value": "linear" + }, + { + "label": "easeInQuad", + "value": "easeInQuad" + }, + { + "label": "easeOutQuad", + "value": "easeOutQuad" + }, + { + "label": "easeInOutQuad", + "value": "easeInOutQuad" + }, + { + "label": "easeInCubic", + "value": "easeInCubic" + }, + { + "label": "easeOutCubic", + "value": "easeOutCubic" + }, + { + "label": "easeInOutCubic", + "value": "easeInOutCubic" + }, + { + "label": "easeInQuart", + "value": "easeInQuart" + }, + { + "label": "easeOutQuart", + "value": "easeOutQuart" + }, + { + "label": "easeInOutQuart", + "value": "easeInOutQuart" + }, + { + "label": "easeInQuint", + "value": "easeInQuint" + }, + { + "label": "easeOutQuint", + "value": "easeOutQuint" + }, + { + "label": "easeInOutQuint", + "value": "easeInOutQuint" + }, + { + "label": "easeInOutSine", + "value": "easeInOutSine" + }, + { + "label": "easeInExpo", + "value": "easeInExpo" + }, + { + "label": "easeOutExpo", + "value": "easeOutExpo" + }, + { + "label": "easeInOutExpo", + "value": "easeInOutExpo" + }, + { + "label": "easeInCirc", + "value": "easeInCirc" + }, + { + "label": "easeOutCirc", + "value": "easeOutCirc" + }, + { + "label": "easeInOutCirc", + "value": "easeInOutCirc" + }, + { + "label": "easeOutBounce", + "value": "easeOutBounce" + }, + { + "label": "easeInBack", + "value": "easeInBack" + }, + { + "label": "easeOutBack", + "value": "easeOutBack" + }, + { + "label": "easeInOutBack", + "value": "easeInOutBack" + }, + { + "label": "elastic", + "value": "elastic" + }, + { + "label": "swingFromTo", + "value": "swingFromTo" + }, + { + "label": "swingFrom", + "value": "swingFrom" + }, + { + "label": "swingTo", + "value": "swingTo" + }, + { + "label": "bounce", + "value": "bounce" + }, + { + "label": "bouncePast", + "value": "bouncePast" + }, + { + "label": "easeFromTo", + "value": "easeFromTo" + }, + { + "label": "easeFrom", + "value": "easeFrom" + }, + { + "label": "easeTo", + "value": "easeTo" + } + ], + "name": "FadeOutEasing" + }, + { + "value": "Idle", + "type": "Choice", + "label": "", + "choices": [ + { + "label": "Idle", + "value": "Idle" + }, + { + "label": "Focused", + "value": "Focused" + }, + { + "label": "Pressed", + "value": "Pressed" + } + ], + "hidden": true, + "name": "PreviousState" + } + ], + "propertiesFolderStructure": { + "folderName": "__ROOT", + "children": [ + { + "propertyName": "ButtonFSM" + }, + { + "propertyName": "Tween" + }, + { + "folderName": "Color", + "children": [ + { + "propertyName": "IdleColorTint" + }, + { + "propertyName": "FocusedColorTint" + }, + { + "propertyName": "PressedColorTint" + } + ] + }, + { + "folderName": "Speed", + "children": [ + { + "propertyName": "FadeInDuration" + }, + { + "propertyName": "FadeOutDuration" + }, + { + "propertyName": "FadeInEasing" + }, + { + "propertyName": "FadeOutEasing" + } + ] + }, + { + "propertyName": "PreviousState" + } + ] + } + } + ], + "eventsBasedObjects": [] } ], "externalLayouts": [] diff --git a/examples/starting-flappy-bird/assets/Background.png b/examples/starting-flappy-bird/assets/Background.png index 83470d7ee7b6b56c474728e849f9b61fc8860c4b..313afed01e19a3496c7e7147c0c31af83fba1bd8 100644 GIT binary patch literal 381 zcmeAS@N?(olHy`uVBq!ia0vp^4Is?H1|$#LC7uRSoCO|{#S9GG!XV7ZFl&wkP>``W z$lZxy-8q?;Kn_c~qpu?a!^VE@KZ&eBK3|DzL`iUdT1k0gQ7VI5W_oVoyp7Y6figcl zT^vIy7~kI6D9Y$4!g|48%RxzDfsv_(<5CuFg$St)+0OzFpLti9Z}fZdcHTYHcU9Xj z>v&zZ+0MoF?&A7m5~n}u?vMSaw0!@)!v%e(+P)W@Iv-m1xCUr40wl!cegAjKcHZ$7 zYf`4Qt@^X==gKRGKmWazdu{a!jpuQnHvgadN&2b&sfnNXU2A@lJ`mkqwgfboFyt=akR{00FX$VgLXD delta 181 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 AZvX%Q diff --git a/examples/starting-flappy-bird/assets/StartingGround.png b/examples/starting-flappy-bird/assets/StartingGround.png index 2a66b69a02b5edb6a6a4bcad3f4f10f259b5d5c2..d181229556ba7aa613300ce4062e7590851e4d06 100644 GIT binary patch delta 655 zcmbQoJco6HWIY=L1H-D!!h1l9v%n*=n1O*?7=#%aX3dcR3NjW4xjQkeJ16rJ$YDu$ z^mSxl*x1kgCy^D%=PPlIC<)F_D=AMbN@Z}%OwUc6w{coB0|S$|r;B4q1>@Tp(Rob) zBCh5gOi|e;Z{6#yX7(*EekowJ&~M@7j7$Cd>U)&*&5<1{o(Z>QE%L-egQopE$#&+sq1rw3 zw96XSX1&ZT{{;5_la=aKJLh@xm%H=?9buNqiiZ-W$Yz_x&REuI@oi(onU9~pwtWkg z&)Z$kep#cue6II@4dx@c`F|F=w74C1`ucSNUemladI{ZiqJ`-khT6_5S5x-Xru==LJ6zxKQn!i3G2er3tr zzWOzz&gpZ1_e~U2X}y_gxcK32EhR4XSFbFCN`6_juKH5nd-wRmyNb!{#LKwnH0;@S z``Zk~(5-dX7%QFK^7QAvTM)qQeQ@oU@0-#@D%smZVt<`#aCTm%ku`VTn#XTsgGygX z`|Iy3>kdPf^#^S1=*B%TqHW49CT25 z&LYuaAdsjq>7YYP;|VS{rEH0g7!DJLS%)21SQ8Zv0Yy2OC-U;R9B@$ij!UgG^8!nt fmc@G+_v^5ojCy3-y$YCs7&ts#{an^LB{Ts5>o^+4 delta 399 zcmV;A0dW4D1)c+t8Gi!+005o0f$RVP01Qw}R7HWZD4D`dlD$KTw>KgR0jK}~010qN zS#tmYE+YT{E+YYWr9XB600B5jL_t(|+U-|S7J@Jg3+?^yEGoj*CZkioc<|waX3a*) zx`^e+3pc?{kO<5HH$9r*9l;7eFPHfQM^6uX|I+*?;t@wj?|+|h#;5o0rYC69jJ-w^ z>~Q3Kx(JDb&lwmJegq)ix=SJNw1=Ro)>NBUb&GOBvRw?QlhMj#jA2t-57S7B`My1QOF|m0P#pN1``D~NLk{iNQ zo5wc?iF1OpqwItCJ_*D>4G+_ZLmlVU;~q6b5%FWW5caQZd7w6Y)pVg|>rA-Tt)aL! tmZ!#ZxZ!KochX!Z{Qk#s6a0hV0d}(x7s>EvasU7T00>D%PDHLkV1jh>v#$UE diff --git a/examples/starting-flappy-bird/assets/StartingPlayer.png b/examples/starting-flappy-bird/assets/StartingPlayer.png index 472f7be2998459c3b5686c8c200d2d72c2046a97..89dd22451ba35db6f10af1b0c18e546774701362 100644 GIT binary patch literal 2978 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 XaJ7e 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 diff --git a/examples/starting-flappy-bird/starting-flappy-bird.json b/examples/starting-flappy-bird/starting-flappy-bird.json index 343ad3b5c..4cf2b088e 100644 --- a/examples/starting-flappy-bird/starting-flappy-bird.json +++ b/examples/starting-flappy-bird/starting-flappy-bird.json @@ -1,7 +1,7 @@ { "firstLayout": "", "gdVersion": { - "build": 272, + "build": 274, "major": 5, "minor": 6, "revision": 0 @@ -130,6 +130,16 @@ "name": "assets\\SpawnPoint.png", "smoothed": true, "userAdded": false + }, + { + "file": "assets/Collision.wav", + "kind": "audio", + "metadata": "", + "name": "assets/Collision.wav", + "preloadAsMusic": false, + "preloadAsSound": false, + "preloadInCache": false, + "userAdded": false } ] }, @@ -370,6 +380,7 @@ "adaptCollisionMaskAutomatically": false, "assetStoreId": "", "name": "Player", + "persistentUuid": "af4ebbc2-d3ef-4703-a33d-bc9ca9d60925", "type": "Sprite", "updateIfNotVisible": false, "variables": [], @@ -394,6 +405,10 @@ "useLegacyTrajectory": false, "xGrabTolerance": 10, "yGrabOffset": 0 + }, + { + "name": "ShakeObject_PositionAngle", + "type": "ShakeObject::ShakeObject_PositionAngle" } ], "animations": [ @@ -474,6 +489,7 @@ "adaptCollisionMaskAutomatically": false, "assetStoreId": "", "name": "SpikeObstacle", + "persistentUuid": "3c77dcd2-99d1-48cd-b1aa-81c464a3e1af", "type": "Sprite", "updateIfNotVisible": false, "variables": [], @@ -506,19 +522,19 @@ "customCollisionMask": [ [ { - "x": 22, - "y": 11 + "x": 26, + "y": 17 }, { - "x": 106, - "y": 11 + "x": 102, + "y": 17 }, { - "x": 106, + "x": 102, "y": 416 }, { - "x": 22, + "x": 26, "y": 416 } ] @@ -682,7 +698,18 @@ }, { "type": "BuiltinCommonInstructions::Standard", - "conditions": [], + "conditions": [ + { + "type": { + "value": "TimeScale" + }, + "parameters": [ + "", + "=", + "1" + ] + } + ], "actions": [ { "type": { @@ -730,9 +757,60 @@ "", "" ] + }, + { + "type": { + "value": "BuiltinCommonInstructions::Once" + }, + "parameters": [] } ], "actions": [ + { + "type": { + "value": "PlaySound" + }, + "parameters": [ + "", + "assets/Collision.wav", + "", + "90", + "RandomFloatInRange(0.7,0.9)" + ] + }, + { + "type": { + "value": "ShakeObject::ShakeObject_PositionAngle::ShakeObject_PositionAngle" + }, + "parameters": [ + "Player", + "ShakeObject_PositionAngle", + "2", + "RandomWithStep(-5,5,10)", + "RandomWithStep(-5,5,10)", + "RandomWithStep(-5,5,10)", + "0.025", + "", + "" + ] + }, + { + "type": { + "value": "ChangeTimeScale" + }, + "parameters": [ + "", + "0.025" + ] + }, + { + "type": { + "value": "Wait" + }, + "parameters": [ + "0.01" + ] + }, { "type": { "value": "Scene" @@ -850,11 +928,4489 @@ { "name": "Scale", "type": "ScalableCapability::ScalableBehavior" + }, + { + "name": "ShakeObject_PositionAngle", + "type": "ShakeObject::ShakeObject_PositionAngle" } ] } ], "externalEvents": [], - "eventsFunctionsExtensions": [], + "eventsFunctionsExtensions": [ + { + "author": "Tristan Rhodes (https://victrisgames.itch.io/)", + "category": "Visual effect", + "dimension": "2D", + "extensionNamespace": "", + "fullName": "Shake object", + "gdevelopVersion": ">=5.5.222", + "helpPath": "", + "iconUrl": "data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iVVRGLTgiPz48IURPQ1RZUEUgc3ZnIFBVQkxJQyAiLS8vVzNDLy9EVEQgU1ZHIDEuMS8vRU4iICJodHRwOi8vd3d3LnczLm9yZy9HcmFwaGljcy9TVkcvMS4xL0RURC9zdmcxMS5kdGQiPjxzdmcgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIiB4bWxuczp4bGluaz0iaHR0cDovL3d3dy53My5vcmcvMTk5OS94bGluayIgdmVyc2lvbj0iMS4xIiBpZD0ibWRpLWFycm93LWFsbCIgd2lkdGg9IjI0IiBoZWlnaHQ9IjI0IiB2aWV3Qm94PSIwIDAgMjQgMjQiPjxwYXRoIGQ9Ik0xMywxMUgxOEwxNi41LDkuNUwxNy45Miw4LjA4TDIxLjg0LDEyTDE3LjkyLDE1LjkyTDE2LjUsMTQuNUwxOCwxM0gxM1YxOEwxNC41LDE2LjVMMTUuOTIsMTcuOTJMMTIsMjEuODRMOC4wOCwxNy45Mkw5LjUsMTYuNUwxMSwxOFYxM0g2TDcuNSwxNC41TDYuMDgsMTUuOTJMMi4xNiwxMkw2LjA4LDguMDhMNy41LDkuNUw2LDExSDExVjZMOS41LDcuNUw4LjA4LDYuMDhMMTIsMi4xNkwxNS45Miw2LjA4TDE0LjUsNy41TDEzLDZWMTFaIiAvPjwvc3ZnPg==", + "name": "ShakeObject", + "previewIconUrl": "https://resources.gdevelop-app.com/assets/Icons/arrow-all.svg", + "shortDescription": "Shake objects with configurable translation, rotation, and scale intensity.", + "version": "1.6.2", + "description": [ + "Shake an object (position, angle or scale).", + "", + "It can be used for:", + "- Hit or impact", + "- Object slowly rocking back and forth (like a ship)", + "- Simulate engine vibration, earthquake, or pulsing", + "", + "The top-down RPG example uses this extension for damage animations ([open the project online](https://editor.gdevelop.io/?project=example://top-down-rpg))." + ], + "origin": { + "identifier": "ShakeObject", + "name": "gdevelop-extension-store" + }, + "tags": [ + "shaking", + "object", + "effect", + "shake", + "scale", + "position", + "rotate", + "angle" + ], + "authorIds": [ + "gqDaZjCfevOOxBYkK6zlhtZnXCg1" + ], + "dependencies": [], + "globalVariables": [], + "sceneVariables": [], + "eventsFunctions": [], + "eventsFunctionsFolderStructure": { + "folderName": "__ROOT" + }, + "eventsBasedBehaviors": [ + { + "description": "Shake objects with translation and rotation.", + "fullName": "Shake object (position, angle)", + "helpPath": "", + "iconUrl": "", + "name": "ShakeObject_PositionAngle", + "objectType": "", + "previewIconUrl": "", + "eventsFunctions": [ + { + "description": "Shake an object, using one or more ways to shake (position, angle). Make sure to \"Stop shaking\" before starting a new shake if it uses different parameters. ", + "fullName": "Shake object (position, angle)", + "functionType": "Action", + "name": "ShakeObject_PositionAngle", + "sentence": "Shake object _PARAM0_ for _PARAM2_ seconds. Modify position amplitude _PARAM3_ on X axis and _PARAM4_ on Y axis, angle rotation amplitude _PARAM5_. Wait _PARAM6_ seconds between shakes. Keep shaking until stopped: _PARAM7_", + "events": [ + { + "colorB": 228, + "colorG": 176, + "colorR": 74, + "creationTime": 0, + "name": "Shake Object", + "source": "", + "type": "BuiltinCommonInstructions::Group", + "events": [ + { + "type": "BuiltinCommonInstructions::Comment", + "color": { + "b": 109, + "g": 230, + "r": 255, + "textB": 0, + "textG": 0, + "textR": 0 + }, + "comment": "Start/Reset duration timer" + }, + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [], + "actions": [ + { + "type": { + "value": "ResetObjectTimer" + }, + "parameters": [ + "Object", + "\"__ShakeObject_DurationTimer\"" + ] + } + ] + }, + { + "type": "BuiltinCommonInstructions::Comment", + "color": { + "b": 109, + "g": 230, + "r": 255, + "textB": 0, + "textG": 0, + "textR": 0 + }, + "comment": "Pass input parameters to global variables so that onScenePostEvents can use them" + }, + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [], + "actions": [ + { + "type": { + "value": "ModVarObjet" + }, + "parameters": [ + "Object", + "__ShakeObject_Duration", + "=", + "Duration" + ] + }, + { + "type": { + "value": "ModVarObjet" + }, + "parameters": [ + "Object", + "__ShakeObject_PowerX", + "=", + "PowerX" + ] + }, + { + "type": { + "value": "ModVarObjet" + }, + "parameters": [ + "Object", + "__ShakeObject_PowerY", + "=", + "PowerY" + ] + }, + { + "type": { + "value": "ModVarObjet" + }, + "parameters": [ + "Object", + "__ShakeObject_PowerAngle", + "=", + "PowerAngle" + ] + }, + { + "type": { + "value": "ModVarObjet" + }, + "parameters": [ + "Object", + "__ShakeObject_TimeBetweenShakes", + "=", + "TimeBetweenShakes" + ] + } + ] + }, + { + "type": "BuiltinCommonInstructions::Comment", + "color": { + "b": 109, + "g": 230, + "r": 255, + "textB": 0, + "textG": 0, + "textR": 0 + }, + "comment": "Determine if the shake should keep going until stopped" + }, + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [], + "actions": [ + { + "type": { + "value": "SetObjectVariableAsBoolean" + }, + "parameters": [ + "Object", + "__ShakeObject_ShakeForever", + "False" + ] + } + ] + }, + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [ + { + "type": { + "value": "BooleanVariable" + }, + "parameters": [ + "ShakeForever", + "True", + "" + ] + } + ], + "actions": [ + { + "type": { + "value": "SetObjectVariableAsBoolean" + }, + "parameters": [ + "Object", + "__ShakeObject_ShakeForever", + "True" + ] + } + ] + }, + { + "type": "BuiltinCommonInstructions::Comment", + "color": { + "b": 109, + "g": 230, + "r": 255, + "textB": 0, + "textG": 0, + "textR": 0 + }, + "comment": "Add default values if none were provided" + }, + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [ + { + "type": { + "value": "VarObjet" + }, + "parameters": [ + "Object", + "__ShakeObject_Duration", + "=", + "0" + ] + } + ], + "actions": [ + { + "type": { + "value": "ModVarObjet" + }, + "parameters": [ + "Object", + "__ShakeObject_Duration", + "=", + "0.5" + ] + } + ] + }, + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [ + { + "type": { + "value": "VarObjet" + }, + "parameters": [ + "Object", + "__ShakeObject_TimeBetweenShakes", + "=", + "0" + ] + } + ], + "actions": [ + { + "type": { + "value": "ModVarObjet" + }, + "parameters": [ + "Object", + "__ShakeObject_TimeBetweenShakes", + "=", + "0.08" + ] + } + ] + }, + { + "type": "BuiltinCommonInstructions::Comment", + "color": { + "b": 109, + "g": 230, + "r": 255, + "textB": 0, + "textG": 0, + "textR": 0 + }, + "comment": "If duration is less than a single shake, increase duration to make 1 full shake" + }, + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [ + { + "type": { + "value": "VarObjet" + }, + "parameters": [ + "Object", + "__ShakeObject_Duration", + "<", + "Object.Variable(__ShakeObject_TimeBetweenShakes)" + ] + } + ], + "actions": [ + { + "type": { + "value": "ModVarObjet" + }, + "parameters": [ + "Object", + "__ShakeObject_Duration", + "=", + "Object.Variable(__ShakeObject_TimeBetweenShakes)" + ] + } + ] + }, + { + "type": "BuiltinCommonInstructions::Comment", + "color": { + "b": 109, + "g": 230, + "r": 255, + "textB": 0, + "textG": 0, + "textR": 0 + }, + "comment": "Detect initial shake" + }, + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [ + { + "type": { + "value": "VarObjet" + }, + "parameters": [ + "Object", + "__ShakeObject_ShakeInProgress", + "=", + "0" + ] + } + ], + "actions": [ + { + "type": { + "value": "ModVarObjet" + }, + "parameters": [ + "Object", + "__ShakeObject_InitialShake", + "=", + "1" + ] + } + ] + }, + { + "type": "BuiltinCommonInstructions::Comment", + "color": { + "b": 109, + "g": 230, + "r": 255, + "textB": 0, + "textG": 0, + "textR": 0 + }, + "comment": "Initiate the onScenePostEvents function" + }, + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [], + "actions": [ + { + "type": { + "value": "ModVarObjet" + }, + "parameters": [ + "Object", + "__ShakeObject_ShakeInProgress", + "=", + "1" + ] + } + ] + } + ], + "parameters": [] + } + ], + "parameters": [ + { + "description": "Object", + "name": "Object", + "type": "object" + }, + { + "description": "Behavior", + "name": "Behavior", + "supplementaryInformation": "ShakeObject::ShakeObject_PositionAngle", + "type": "behavior" + }, + { + "description": "Duration of shake (in seconds) (Default: 0.5) ", + "name": "Duration", + "type": "expression" + }, + { + "description": "Amplitude of postion shake in X direction (in pixels) (For example: 5)", + "name": "PowerX", + "type": "expression" + }, + { + "description": "Amplitude of position shake in Y direction (in pixels) (For example: 5)", + "longDescription": "Use a negative number to make the single-shake move in the opposite direction.", + "name": "PowerY", + "type": "expression" + }, + { + "description": "Amplitude of angle rotation shake (in degrees) (For example: 5)", + "name": "PowerAngle", + "type": "expression" + }, + { + "description": "Amount of time between shakes (in seconds) (Default: 0.08)", + "longDescription": "For a single-shake effect, set it to the same value as \"Duration\".", + "name": "TimeBetweenShakes", + "type": "expression" + }, + { + "description": "Keep shaking until stopped", + "longDescription": "Duration value will be ignored", + "name": "ShakeForever", + "type": "yesorno" + } + ], + "objectGroups": [] + }, + { + "fullName": "", + "functionType": "Action", + "name": "doStepPostEvents", + "sentence": "", + "events": [ + { + "colorB": 228, + "colorG": 176, + "colorR": 74, + "creationTime": 0, + "name": "Shake Object", + "source": "", + "type": "BuiltinCommonInstructions::Group", + "events": [ + { + "colorB": 228, + "colorG": 176, + "colorR": 74, + "creationTime": 0, + "name": "Start shaking", + "source": "", + "type": "BuiltinCommonInstructions::Group", + "events": [ + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [ + { + "type": { + "value": "VarObjet" + }, + "parameters": [ + "Object", + "__ShakeObject_ShakeInProgress", + "=", + "1" + ] + } + ], + "actions": [], + "events": [ + { + "type": "BuiltinCommonInstructions::Comment", + "color": { + "b": 109, + "g": 230, + "r": 255, + "textB": 0, + "textG": 0, + "textR": 0 + }, + "comment": "Keep object shaking forever (if desired)" + }, + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [ + { + "type": { + "value": "ObjectVariableAsBoolean" + }, + "parameters": [ + "Object", + "__ShakeObject_ShakeForever", + "True" + ] + } + ], + "actions": [ + { + "type": { + "value": "ModVarObjet" + }, + "parameters": [ + "Object", + "__ShakeObject_Duration", + "=", + "100" + ] + }, + { + "type": { + "value": "ResetObjectTimer" + }, + "parameters": [ + "Object", + "\"__ShakeObject_DurationTimer\"" + ] + } + ] + }, + { + "colorB": 228, + "colorG": 176, + "colorR": 74, + "creationTime": 0, + "name": "Calculate movement of the shake", + "source": "", + "type": "BuiltinCommonInstructions::Group", + "events": [ + { + "type": "BuiltinCommonInstructions::Comment", + "color": { + "b": 109, + "g": 230, + "r": 255, + "textB": 0, + "textG": 0, + "textR": 0 + }, + "comment": "Run once before every shake movement" + }, + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [ + { + "type": { + "value": "BuiltinCommonInstructions::Or" + }, + "parameters": [], + "subInstructions": [ + { + "type": { + "value": "VarObjet" + }, + "parameters": [ + "Object", + "__ShakeObject_InitialShake", + "=", + "1" + ] + }, + { + "type": { + "value": "ObjectTimer" + }, + "parameters": [ + "Object", + "\"__ShakeObject_ShakeTimer\"", + "Object.Variable(__ShakeObject_TimeBetweenShakes)" + ] + } + ] + } + ], + "actions": [ + { + "type": { + "value": "ResetObjectTimer" + }, + "parameters": [ + "Object", + "\"__ShakeObject_ShakeTimer\"" + ] + }, + { + "type": { + "value": "ModVarObjet" + }, + "parameters": [ + "Object", + "__ShakeObject_ShakeCounter", + "+", + "1" + ] + } + ], + "events": [ + { + "colorB": 228, + "colorG": 176, + "colorR": 74, + "creationTime": 0, + "name": "Correct for drift and reset drift tracking variables", + "source": "", + "type": "BuiltinCommonInstructions::Group", + "events": [ + { + "type": "BuiltinCommonInstructions::Comment", + "color": { + "b": 109, + "g": 230, + "r": 255, + "textB": 0, + "textG": 0, + "textR": 0 + }, + "comment": "Move to correct any drift from previous shake" + }, + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [ + { + "type": { + "value": "BuiltinCommonInstructions::Or" + }, + "parameters": [], + "subInstructions": [ + { + "type": { + "value": "VarObjet" + }, + "parameters": [ + "Object", + "__ShakeObject_PowerX", + "!=", + "0" + ] + }, + { + "type": { + "value": "VarObjet" + }, + "parameters": [ + "Object", + "__ShakeObject_PowerY", + "!=", + "0" + ] + } + ] + } + ], + "actions": [ + { + "type": { + "value": "SetX" + }, + "parameters": [ + "Object", + "-", + "Object.Variable(__ShakeObject_DisplacementTravelledX)" + ] + }, + { + "type": { + "value": "SetY" + }, + "parameters": [ + "Object", + "-", + "Object.Variable(__ShakeObject_DisplacementTravelledY)" + ] + } + ] + }, + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [ + { + "type": { + "value": "VarObjet" + }, + "parameters": [ + "Object", + "__ShakeObject_PowerAngle", + "!=", + "0" + ] + } + ], + "actions": [ + { + "type": { + "value": "SetAngle" + }, + "parameters": [ + "Object", + "-", + "Object.Variable(__ShakeObject_AngleTravelled)" + ] + } + ] + }, + { + "type": "BuiltinCommonInstructions::Comment", + "color": { + "b": 109, + "g": 230, + "r": 255, + "textB": 0, + "textG": 0, + "textR": 0 + }, + "comment": "Reset drift detection variables" + }, + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [], + "actions": [ + { + "type": { + "value": "ModVarObjet" + }, + "parameters": [ + "Object", + "__ShakeObject_DisplacementTravelledX", + "=", + "0" + ] + }, + { + "type": { + "value": "ModVarObjet" + }, + "parameters": [ + "Object", + "__ShakeObject_DisplacementTravelledY", + "=", + "0" + ] + }, + { + "type": { + "value": "ModVarObjet" + }, + "parameters": [ + "Object", + "__ShakeObject_AngleTravelled", + "=", + "0" + ] + } + ] + } + ], + "parameters": [] + }, + { + "type": "BuiltinCommonInstructions::Comment", + "color": { + "b": 109, + "g": 230, + "r": 255, + "textB": 0, + "textG": 0, + "textR": 0 + }, + "comment": "Calculate Position Shake" + }, + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [ + { + "type": { + "value": "BuiltinCommonInstructions::Or" + }, + "parameters": [], + "subInstructions": [ + { + "type": { + "value": "VarObjet" + }, + "parameters": [ + "Object", + "__ShakeObject_PowerX", + "!=", + "0" + ] + }, + { + "type": { + "value": "VarObjet" + }, + "parameters": [ + "Object", + "__ShakeObject_PowerY", + "!=", + "0" + ] + } + ] + } + ], + "actions": [], + "events": [ + { + "type": "BuiltinCommonInstructions::Comment", + "color": { + "b": 109, + "g": 230, + "r": 255, + "textB": 0, + "textG": 0, + "textR": 0 + }, + "comment": "PositionDisplacement = (DesiredDuration - RunningTimer) / DesiredDuration * Amplitude * [-1 or 1]" + }, + { + "type": "BuiltinCommonInstructions::Comment", + "color": { + "b": 109, + "g": 230, + "r": 255, + "textB": 0, + "textG": 0, + "textR": 0 + }, + "comment": "Make initial shake NOT random so users can set a direction for a one-shake effect" + }, + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [ + { + "type": { + "value": "VarObjet" + }, + "parameters": [ + "Object", + "__ShakeObject_InitialShake", + "=", + "1" + ] + } + ], + "actions": [], + "events": [ + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [ + { + "type": { + "value": "VarObjet" + }, + "parameters": [ + "Object", + "__ShakeObject_PowerX", + "!=", + "0" + ] + } + ], + "actions": [ + { + "type": { + "value": "ModVarObjet" + }, + "parameters": [ + "Object", + "__ShakeObject_DisplacementX", + "=", + "(Object.Variable(__ShakeObject_Duration) - Object.ObjectTimerElapsedTime(\"__ShakeObject_DurationTimer\")) / Object.Variable(__ShakeObject_Duration) * Object.Variable(__ShakeObject_PowerX)" + ] + } + ] + }, + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [ + { + "type": { + "value": "VarObjet" + }, + "parameters": [ + "Object", + "__ShakeObject_PowerY", + "!=", + "0" + ] + } + ], + "actions": [ + { + "type": { + "value": "ModVarObjet" + }, + "parameters": [ + "Object", + "__ShakeObject_DisplacementY", + "=", + "(Object.Variable(__ShakeObject_Duration) - Object.ObjectTimerElapsedTime(\"__ShakeObject_DurationTimer\")) / Object.Variable(__ShakeObject_Duration) * Object.Variable(__ShakeObject_PowerY)" + ] + } + ] + } + ] + }, + { + "type": "BuiltinCommonInstructions::Comment", + "color": { + "b": 109, + "g": 230, + "r": 255, + "textB": 0, + "textG": 0, + "textR": 0 + }, + "comment": "After initial shake pick a random direction" + }, + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [ + { + "type": { + "value": "VarObjet" + }, + "parameters": [ + "Object", + "__ShakeObject_InitialShake", + "=", + "0" + ] + } + ], + "actions": [], + "events": [ + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [ + { + "type": { + "value": "VarObjet" + }, + "parameters": [ + "Object", + "__ShakeObject_PowerX", + "!=", + "0" + ] + } + ], + "actions": [ + { + "type": { + "value": "ModVarObjet" + }, + "parameters": [ + "Object", + "__ShakeObject_DisplacementX", + "=", + "(Object.Variable(__ShakeObject_Duration) - Object.ObjectTimerElapsedTime(\"__ShakeObject_DurationTimer\")) / Object.Variable(__ShakeObject_Duration) * Object.Variable(__ShakeObject_PowerX) * RandomWithStep(-1, 1, 2)" + ] + } + ] + }, + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [ + { + "type": { + "value": "VarObjet" + }, + "parameters": [ + "Object", + "__ShakeObject_PowerY", + "!=", + "0" + ] + } + ], + "actions": [ + { + "type": { + "value": "ModVarObjet" + }, + "parameters": [ + "Object", + "__ShakeObject_DisplacementY", + "=", + "(Object.Variable(__ShakeObject_Duration) - Object.ObjectTimerElapsedTime(\"__ShakeObject_DurationTimer\")) / Object.Variable(__ShakeObject_Duration) * Object.Variable(__ShakeObject_PowerY) * RandomWithStep(-1, 1, 2)" + ] + } + ] + } + ] + } + ] + }, + { + "type": "BuiltinCommonInstructions::Comment", + "color": { + "b": 109, + "g": 230, + "r": 255, + "textB": 0, + "textG": 0, + "textR": 0 + }, + "comment": "Calculate Rotation (angle) shake " + }, + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [ + { + "type": { + "value": "VarObjet" + }, + "parameters": [ + "Object", + "__ShakeObject_PowerAngle", + "!=", + "0" + ] + } + ], + "actions": [], + "events": [ + { + "type": "BuiltinCommonInstructions::Comment", + "color": { + "b": 109, + "g": 230, + "r": 255, + "textB": 0, + "textG": 0, + "textR": 0 + }, + "comment": "Every \"even\" shake, rotate clockwise" + }, + { + "type": "BuiltinCommonInstructions::Comment", + "color": { + "b": 109, + "g": 230, + "r": 255, + "textB": 0, + "textG": 0, + "textR": 0 + }, + "comment": "AngleDisplacement = (DesiredDuration - RunningTimer) / DesiredDuration * Amplitude" + }, + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [ + { + "type": { + "value": "BuiltinCommonInstructions::CompareNumbers" + }, + "parameters": [ + "mod(Object.Variable(__ShakeObject_ShakeCounter),2)", + "=", + "0" + ] + } + ], + "actions": [ + { + "type": { + "value": "ModVarObjet" + }, + "parameters": [ + "Object", + "__ShakeObject_DisplacementAngle", + "=", + "(Object.Variable(__ShakeObject_Duration) - Object.ObjectTimerElapsedTime(\"__ShakeObject_DurationTimer\")) / Object.Variable(__ShakeObject_Duration) * Object.Variable(__ShakeObject_PowerAngle)" + ] + } + ] + }, + { + "type": "BuiltinCommonInstructions::Comment", + "color": { + "b": 109, + "g": 230, + "r": 255, + "textB": 0, + "textG": 0, + "textR": 0 + }, + "comment": "Every \"odd\" shake, rotate counter-clockwise" + }, + { + "type": "BuiltinCommonInstructions::Comment", + "color": { + "b": 109, + "g": 230, + "r": 255, + "textB": 0, + "textG": 0, + "textR": 0 + }, + "comment": "AngleDisplacement = (DesiredDuration - RunningTimer) / DesiredDuration * Amplitude" + }, + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [ + { + "type": { + "value": "BuiltinCommonInstructions::CompareNumbers" + }, + "parameters": [ + "mod(Object.Variable(__ShakeObject_ShakeCounter),2)", + "=", + "1" + ] + } + ], + "actions": [ + { + "type": { + "value": "ModVarObjet" + }, + "parameters": [ + "Object", + "__ShakeObject_DisplacementAngle", + "=", + "-1 *(Object.Variable(__ShakeObject_Duration) - Object.ObjectTimerElapsedTime(\"__ShakeObject_DurationTimer\")) / Object.Variable(__ShakeObject_Duration) * Object.Variable(__ShakeObject_PowerAngle)" + ] + } + ] + } + ] + }, + { + "type": "BuiltinCommonInstructions::Comment", + "color": { + "b": 109, + "g": 230, + "r": 255, + "textB": 0, + "textG": 0, + "textR": 0 + }, + "comment": "Save that initial shake has been processed" + }, + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [], + "actions": [ + { + "type": { + "value": "ModVarObjet" + }, + "parameters": [ + "Object", + "__ShakeObject_InitialShake", + "=", + "0" + ] + } + ] + } + ] + } + ], + "parameters": [] + }, + { + "colorB": 228, + "colorG": 176, + "colorR": 74, + "creationTime": 0, + "name": "Move object", + "source": "", + "type": "BuiltinCommonInstructions::Group", + "events": [ + { + "type": "BuiltinCommonInstructions::Comment", + "color": { + "b": 109, + "g": 230, + "r": 255, + "textB": 0, + "textG": 0, + "textR": 0 + }, + "comment": "Calculate the fraction of shake that occured during this frame" + }, + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [], + "actions": [ + { + "type": { + "value": "ModVarObjet" + }, + "parameters": [ + "Object", + "__ShakeObject_PercentTimeElapsedThisFrame", + "=", + "min(1,TimeDelta()/Object.Variable(__ShakeObject_TimeBetweenShakes))" + ] + } + ] + }, + { + "type": "BuiltinCommonInstructions::Comment", + "color": { + "b": 109, + "g": 230, + "r": 255, + "textB": 0, + "textG": 0, + "textR": 0 + }, + "comment": "Increase change for the first half of the shake (move away from original values)" + }, + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [ + { + "type": { + "inverted": true, + "value": "ObjectTimer" + }, + "parameters": [ + "Object", + "\"__ShakeObject_ShakeTimer\"", + "Object.Variable(__ShakeObject_TimeBetweenShakes)/2" + ] + } + ], + "actions": [], + "events": [ + { + "type": "BuiltinCommonInstructions::Comment", + "color": { + "b": 109, + "g": 230, + "r": 255, + "textB": 0, + "textG": 0, + "textR": 0 + }, + "comment": "Change position" + }, + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [ + { + "type": { + "value": "BuiltinCommonInstructions::Or" + }, + "parameters": [], + "subInstructions": [ + { + "type": { + "value": "VarObjet" + }, + "parameters": [ + "Object", + "__ShakeObject_PowerX", + "!=", + "0" + ] + }, + { + "type": { + "value": "VarObjet" + }, + "parameters": [ + "Object", + "__ShakeObject_PowerY", + "!=", + "0" + ] + } + ] + } + ], + "actions": [ + { + "type": { + "value": "SetX" + }, + "parameters": [ + "Object", + "+", + "round(1024 * Object.Variable(__ShakeObject_DisplacementX) * Object.Variable(__ShakeObject_PercentTimeElapsedThisFrame)) / 1024" + ] + }, + { + "type": { + "value": "SetY" + }, + "parameters": [ + "Object", + "+", + "round(1024 * Object.Variable(__ShakeObject_DisplacementY) * Object.Variable(__ShakeObject_PercentTimeElapsedThisFrame)) / 1024" + ] + } + ], + "events": [ + { + "type": "BuiltinCommonInstructions::Comment", + "color": { + "b": 109, + "g": 230, + "r": 255, + "textB": 0, + "textG": 0, + "textR": 0 + }, + "comment": "Save movement to calculate drift" + }, + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [], + "actions": [ + { + "type": { + "value": "ModVarObjet" + }, + "parameters": [ + "Object", + "__ShakeObject_DisplacementTravelledX", + "+", + "round(1024 * Object.Variable(__ShakeObject_DisplacementX) * Object.Variable(__ShakeObject_PercentTimeElapsedThisFrame)) / 1024" + ] + }, + { + "type": { + "value": "ModVarObjet" + }, + "parameters": [ + "Object", + "__ShakeObject_DisplacementTravelledY", + "+", + "round(1024 * Object.Variable(__ShakeObject_DisplacementY) * Object.Variable(__ShakeObject_PercentTimeElapsedThisFrame)) / 1024" + ] + } + ] + } + ] + }, + { + "type": "BuiltinCommonInstructions::Comment", + "color": { + "b": 109, + "g": 230, + "r": 255, + "textB": 0, + "textG": 0, + "textR": 0 + }, + "comment": "Change angle" + }, + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [ + { + "type": { + "value": "BuiltinCommonInstructions::CompareNumbers" + }, + "parameters": [ + "Object.Variable(__ShakeObject_PowerAngle)", + "!=", + "0" + ] + } + ], + "actions": [ + { + "type": { + "value": "SetAngle" + }, + "parameters": [ + "Object", + "+", + "round(1024 * Object.Variable(__ShakeObject_DisplacementAngle) * Object.Variable(__ShakeObject_PercentTimeElapsedThisFrame)) / 1024" + ] + } + ], + "events": [ + { + "type": "BuiltinCommonInstructions::Comment", + "color": { + "b": 109, + "g": 230, + "r": 255, + "textB": 0, + "textG": 0, + "textR": 0 + }, + "comment": "Save movement to calculate drift" + }, + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [], + "actions": [ + { + "type": { + "value": "ModVarObjet" + }, + "parameters": [ + "Object", + "__ShakeObject_AngleTravelled", + "+", + "round(1024 * Object.Variable(__ShakeObject_DisplacementAngle) * Object.Variable(__ShakeObject_PercentTimeElapsedThisFrame)) / 1024" + ] + } + ] + } + ] + } + ] + }, + { + "type": "BuiltinCommonInstructions::Comment", + "color": { + "b": 109, + "g": 230, + "r": 255, + "textB": 0, + "textG": 0, + "textR": 0 + }, + "comment": "Decrease change the second half of the shake (return to original position)" + }, + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [ + { + "type": { + "value": "ObjectTimer" + }, + "parameters": [ + "Object", + "\"__ShakeObject_ShakeTimer\"", + "Object.Variable(__ShakeObject_TimeBetweenShakes)/2" + ] + } + ], + "actions": [], + "events": [ + { + "type": "BuiltinCommonInstructions::Comment", + "color": { + "b": 109, + "g": 230, + "r": 255, + "textB": 0, + "textG": 0, + "textR": 0 + }, + "comment": "Change position" + }, + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [ + { + "type": { + "value": "BuiltinCommonInstructions::Or" + }, + "parameters": [], + "subInstructions": [ + { + "type": { + "value": "VarObjet" + }, + "parameters": [ + "Object", + "__ShakeObject_PowerX", + "!=", + "0" + ] + }, + { + "type": { + "value": "VarObjet" + }, + "parameters": [ + "Object", + "__ShakeObject_PowerY", + "!=", + "0" + ] + } + ] + } + ], + "actions": [ + { + "type": { + "value": "SetX" + }, + "parameters": [ + "Object", + "-", + "round(1024 * Object.Variable(__ShakeObject_DisplacementX) * Object.Variable(__ShakeObject_PercentTimeElapsedThisFrame)) / 1024" + ] + }, + { + "type": { + "value": "SetY" + }, + "parameters": [ + "Object", + "-", + "round(1024 * Object.Variable(__ShakeObject_DisplacementY) * Object.Variable(__ShakeObject_PercentTimeElapsedThisFrame)) / 1024" + ] + } + ], + "events": [ + { + "type": "BuiltinCommonInstructions::Comment", + "color": { + "b": 109, + "g": 230, + "r": 255, + "textB": 0, + "textG": 0, + "textR": 0 + }, + "comment": "Save movement to calculate drift" + }, + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [], + "actions": [ + { + "type": { + "value": "ModVarObjet" + }, + "parameters": [ + "Object", + "__ShakeObject_DisplacementTravelledX", + "-", + "round(1024 * Object.Variable(__ShakeObject_DisplacementX) * Object.Variable(__ShakeObject_PercentTimeElapsedThisFrame)) / 1024" + ] + }, + { + "type": { + "value": "ModVarObjet" + }, + "parameters": [ + "Object", + "__ShakeObject_DisplacementTravelledY", + "-", + "round(1024 * Object.Variable(__ShakeObject_DisplacementY) * Object.Variable(__ShakeObject_PercentTimeElapsedThisFrame)) / 1024" + ] + } + ] + } + ] + }, + { + "type": "BuiltinCommonInstructions::Comment", + "color": { + "b": 109, + "g": 230, + "r": 255, + "textB": 0, + "textG": 0, + "textR": 0 + }, + "comment": "Change angle" + }, + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [ + { + "type": { + "value": "BuiltinCommonInstructions::CompareNumbers" + }, + "parameters": [ + "Object.Variable(__ShakeObject_PowerAngle)", + "!=", + "0" + ] + } + ], + "actions": [ + { + "type": { + "value": "SetAngle" + }, + "parameters": [ + "Object", + "-", + "round(1024 * Object.Variable(__ShakeObject_DisplacementAngle) * Object.Variable(__ShakeObject_PercentTimeElapsedThisFrame)) / 1024" + ] + } + ], + "events": [ + { + "type": "BuiltinCommonInstructions::Comment", + "color": { + "b": 109, + "g": 230, + "r": 255, + "textB": 0, + "textG": 0, + "textR": 0 + }, + "comment": "Save movement to calculate drift" + }, + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [], + "actions": [ + { + "type": { + "value": "ModVarObjet" + }, + "parameters": [ + "Object", + "__ShakeObject_AngleTravelled", + "-", + "round(1024 * Object.Variable(__ShakeObject_DisplacementAngle) * Object.Variable(__ShakeObject_PercentTimeElapsedThisFrame)) / 1024" + ] + } + ] + } + ] + } + ] + } + ], + "parameters": [] + } + ] + } + ], + "parameters": [] + }, + { + "colorB": 228, + "colorG": 176, + "colorR": 74, + "creationTime": 0, + "name": "Stop shaking", + "source": "", + "type": "BuiltinCommonInstructions::Group", + "events": [ + { + "type": "BuiltinCommonInstructions::Comment", + "color": { + "b": 109, + "g": 230, + "r": 255, + "textB": 0, + "textG": 0, + "textR": 0 + }, + "comment": "Stop shaking when the duration has been reached (or if the stop shaking function was called)" + }, + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [ + { + "type": { + "value": "BuiltinCommonInstructions::Or" + }, + "parameters": [], + "subInstructions": [ + { + "type": { + "value": "ObjectTimer" + }, + "parameters": [ + "Object", + "\"__ShakeObject_DurationTimer\"", + "Object.Variable(__ShakeObject_Duration)" + ] + }, + { + "type": { + "value": "VarObjet" + }, + "parameters": [ + "Object", + "__ShakeObject_ShakeInProgress", + "=", + "-1" + ] + } + ] + }, + { + "type": { + "value": "BuiltinCommonInstructions::Once" + }, + "parameters": [] + } + ], + "actions": [ + { + "type": { + "value": "ModVarObjet" + }, + "parameters": [ + "Object", + "__ShakeObject_ShakeInProgress", + "=", + "0" + ] + }, + { + "type": { + "value": "ModVarObjet" + }, + "parameters": [ + "Object", + "__ShakeObject_ShakeCounter", + "=", + "0" + ] + } + ], + "events": [ + { + "colorB": 228, + "colorG": 176, + "colorR": 74, + "creationTime": 0, + "name": "Correct for drift and reset drift tracking variables", + "source": "", + "type": "BuiltinCommonInstructions::Group", + "events": [ + { + "type": "BuiltinCommonInstructions::Comment", + "color": { + "b": 109, + "g": 230, + "r": 255, + "textB": 0, + "textG": 0, + "textR": 0 + }, + "comment": "Move to correct any drift from previous shake" + }, + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [ + { + "type": { + "value": "BuiltinCommonInstructions::Or" + }, + "parameters": [], + "subInstructions": [ + { + "type": { + "value": "VarObjet" + }, + "parameters": [ + "Object", + "__ShakeObject_PowerX", + "!=", + "0" + ] + }, + { + "type": { + "value": "VarObjet" + }, + "parameters": [ + "Object", + "__ShakeObject_PowerY", + "!=", + "0" + ] + } + ] + } + ], + "actions": [ + { + "type": { + "value": "SetX" + }, + "parameters": [ + "Object", + "-", + "Object.Variable(__ShakeObject_DisplacementTravelledX)" + ] + }, + { + "type": { + "value": "SetY" + }, + "parameters": [ + "Object", + "-", + "Object.Variable(__ShakeObject_DisplacementTravelledY)" + ] + } + ] + }, + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [ + { + "type": { + "value": "VarObjet" + }, + "parameters": [ + "Object", + "__ShakeObject_PowerAngle", + "!=", + "0" + ] + } + ], + "actions": [ + { + "type": { + "value": "SetAngle" + }, + "parameters": [ + "Object", + "-", + "Object.Variable(__ShakeObject_AngleTravelled)" + ] + } + ] + }, + { + "type": "BuiltinCommonInstructions::Comment", + "color": { + "b": 109, + "g": 230, + "r": 255, + "textB": 0, + "textG": 0, + "textR": 0 + }, + "comment": "Reset drift detection variables" + }, + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [], + "actions": [ + { + "type": { + "value": "ModVarObjet" + }, + "parameters": [ + "Object", + "__ShakeObject_DisplacementTravelledX", + "=", + "0" + ] + }, + { + "type": { + "value": "ModVarObjet" + }, + "parameters": [ + "Object", + "__ShakeObject_DisplacementTravelledY", + "=", + "0" + ] + }, + { + "type": { + "value": "ModVarObjet" + }, + "parameters": [ + "Object", + "__ShakeObject_AngleTravelled", + "=", + "0" + ] + } + ] + } + ], + "parameters": [] + } + ] + } + ], + "parameters": [] + } + ], + "parameters": [] + } + ], + "parameters": [ + { + "description": "Object", + "name": "Object", + "type": "object" + }, + { + "description": "Behavior", + "name": "Behavior", + "supplementaryInformation": "ShakeObject::ShakeObject_PositionAngle", + "type": "behavior" + } + ], + "objectGroups": [] + }, + { + "description": "Stop any shaking of object that was initiated by the Shake Object extension.", + "fullName": "Stop shaking the object", + "functionType": "Action", + "name": "StopShaking", + "sentence": "Stop shaking _PARAM0_", + "events": [ + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [ + { + "type": { + "value": "ShakeObject::ShakeObject_PositionAngle::IsShaking" + }, + "parameters": [ + "Object", + "Behavior", + "" + ] + } + ], + "actions": [ + { + "type": { + "value": "ModVarObjet" + }, + "parameters": [ + "Object", + "__ShakeObject_ShakeInProgress", + "=", + "-1" + ] + } + ] + } + ], + "parameters": [ + { + "description": "Object", + "name": "Object", + "type": "object" + }, + { + "description": "Behavior", + "name": "Behavior", + "supplementaryInformation": "ShakeObject::ShakeObject_PositionAngle", + "type": "behavior" + } + ], + "objectGroups": [] + }, + { + "description": "Check if an object is shaking.", + "fullName": "Check if an object is shaking", + "functionType": "Condition", + "name": "IsShaking", + "sentence": "_PARAM0_ is shaking", + "events": [ + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [ + { + "type": { + "value": "VarObjet" + }, + "parameters": [ + "Object", + "__ShakeObject_ShakeInProgress", + "=", + "1" + ] + } + ], + "actions": [ + { + "type": { + "value": "SetReturnBoolean" + }, + "parameters": [ + "True" + ] + } + ] + } + ], + "parameters": [ + { + "description": "Object", + "name": "Object", + "type": "object" + }, + { + "description": "Behavior", + "name": "Behavior", + "supplementaryInformation": "ShakeObject::ShakeObject_PositionAngle", + "type": "behavior" + } + ], + "objectGroups": [] + } + ], + "eventsFunctionsFolderStructure": { + "folderName": "__ROOT", + "children": [ + { + "functionName": "ShakeObject_PositionAngle" + }, + { + "functionName": "doStepPostEvents" + }, + { + "functionName": "StopShaking" + }, + { + "functionName": "IsShaking" + } + ] + }, + "propertyDescriptors": [], + "propertiesFolderStructure": { + "folderName": "__ROOT" + } + }, + { + "description": "Shake objects with translation, rotation and scale.", + "fullName": "Shake object (position, angle, scale)", + "helpPath": "", + "iconUrl": "", + "name": "ShakeObject_PositionAngleScale", + "objectType": "", + "previewIconUrl": "", + "eventsFunctions": [ + { + "description": "Shake an object, using one or more ways to shake (position, angle, scale). Make sure to \"Stop shaking\" before starting a new shake if it uses different parameters.", + "fullName": "Shake object (position, angle, scale)", + "functionType": "Action", + "name": "ShakeObject_PositionAngleScale", + "sentence": "Shake object _PARAM0_ for _PARAM2_ seconds. Modify position amplitude _PARAM3_ on X axis and _PARAM4_ on Y axis, angle rotation amplitude _PARAM5_, and scale amplitude _PARAM6_. Wait _PARAM7_ seconds between shakes. Keep shaking until stopped: _PARAM8_", + "events": [ + { + "colorB": 228, + "colorG": 176, + "colorR": 74, + "creationTime": 0, + "name": "Shake Object", + "source": "", + "type": "BuiltinCommonInstructions::Group", + "events": [ + { + "type": "BuiltinCommonInstructions::Comment", + "color": { + "b": 109, + "g": 230, + "r": 255, + "textB": 0, + "textG": 0, + "textR": 0 + }, + "comment": "Start/Reset duration timer" + }, + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [], + "actions": [ + { + "type": { + "value": "ResetObjectTimer" + }, + "parameters": [ + "Object", + "\"__ShakeObject_DurationTimer\"" + ] + } + ] + }, + { + "type": "BuiltinCommonInstructions::Comment", + "color": { + "b": 109, + "g": 230, + "r": 255, + "textB": 0, + "textG": 0, + "textR": 0 + }, + "comment": "Pass input parameters to global variables so that onScenePostEvents can use them" + }, + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [], + "actions": [ + { + "type": { + "value": "ModVarObjet" + }, + "parameters": [ + "Object", + "__ShakeObject_Duration", + "=", + "Duration" + ] + }, + { + "type": { + "value": "ModVarObjet" + }, + "parameters": [ + "Object", + "__ShakeObject_PowerX", + "=", + "PowerX" + ] + }, + { + "type": { + "value": "ModVarObjet" + }, + "parameters": [ + "Object", + "__ShakeObject_PowerY", + "=", + "PowerY" + ] + }, + { + "type": { + "value": "ModVarObjet" + }, + "parameters": [ + "Object", + "__ShakeObject_PowerAngle", + "=", + "PowerAngle" + ] + }, + { + "type": { + "value": "ModVarObjet" + }, + "parameters": [ + "Object", + "__ShakeObject_PowerScale", + "=", + "PowerScale" + ] + }, + { + "type": { + "value": "ModVarObjet" + }, + "parameters": [ + "Object", + "__ShakeObject_TimeBetweenShakes", + "=", + "TimeBetweenShakes" + ] + } + ] + }, + { + "type": "BuiltinCommonInstructions::Comment", + "color": { + "b": 109, + "g": 230, + "r": 255, + "textB": 0, + "textG": 0, + "textR": 0 + }, + "comment": "Determine if the shake should keep going until stopped" + }, + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [], + "actions": [ + { + "type": { + "value": "SetObjectVariableAsBoolean" + }, + "parameters": [ + "Object", + "__ShakeObject_ShakeForever", + "False" + ] + } + ] + }, + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [ + { + "type": { + "value": "BooleanVariable" + }, + "parameters": [ + "ShakeForever", + "True", + "" + ] + } + ], + "actions": [ + { + "type": { + "value": "SetObjectVariableAsBoolean" + }, + "parameters": [ + "Object", + "__ShakeObject_ShakeForever", + "True" + ] + } + ] + }, + { + "type": "BuiltinCommonInstructions::Comment", + "color": { + "b": 109, + "g": 230, + "r": 255, + "textB": 0, + "textG": 0, + "textR": 0 + }, + "comment": "Add default values if none were provided" + }, + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [ + { + "type": { + "value": "VarObjet" + }, + "parameters": [ + "Object", + "__ShakeObject_Duration", + "=", + "0" + ] + } + ], + "actions": [ + { + "type": { + "value": "ModVarObjet" + }, + "parameters": [ + "Object", + "__ShakeObject_Duration", + "=", + "0.5" + ] + } + ] + }, + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [ + { + "type": { + "value": "VarObjet" + }, + "parameters": [ + "Object", + "__ShakeObject_TimeBetweenShakes", + "=", + "0" + ] + } + ], + "actions": [ + { + "type": { + "value": "ModVarObjet" + }, + "parameters": [ + "Object", + "__ShakeObject_TimeBetweenShakes", + "=", + "0.08" + ] + } + ] + }, + { + "type": "BuiltinCommonInstructions::Comment", + "color": { + "b": 109, + "g": 230, + "r": 255, + "textB": 0, + "textG": 0, + "textR": 0 + }, + "comment": "If duration is less than a single shake, increase duration to make 1 full shake" + }, + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [ + { + "type": { + "value": "VarObjet" + }, + "parameters": [ + "Object", + "__ShakeObject_Duration", + "<", + "Object.Variable(__ShakeObject_TimeBetweenShakes)" + ] + } + ], + "actions": [ + { + "type": { + "value": "ModVarObjet" + }, + "parameters": [ + "Object", + "__ShakeObject_Duration", + "=", + "Object.Variable(__ShakeObject_TimeBetweenShakes)" + ] + } + ] + }, + { + "type": "BuiltinCommonInstructions::Comment", + "color": { + "b": 109, + "g": 230, + "r": 255, + "textB": 0, + "textG": 0, + "textR": 0 + }, + "comment": "Detect initial shake" + }, + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [ + { + "type": { + "value": "VarObjet" + }, + "parameters": [ + "Object", + "__ShakeObject_ShakeInProgress", + "=", + "0" + ] + } + ], + "actions": [ + { + "type": { + "value": "ModVarObjet" + }, + "parameters": [ + "Object", + "__ShakeObject_InitialShake", + "=", + "1" + ] + } + ] + }, + { + "type": "BuiltinCommonInstructions::Comment", + "color": { + "b": 109, + "g": 230, + "r": 255, + "textB": 0, + "textG": 0, + "textR": 0 + }, + "comment": "Initiate the onScenePostEvents function" + }, + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [], + "actions": [ + { + "type": { + "value": "ModVarObjet" + }, + "parameters": [ + "Object", + "__ShakeObject_ShakeInProgress", + "=", + "1" + ] + } + ] + } + ], + "parameters": [] + } + ], + "parameters": [ + { + "description": "Object", + "name": "Object", + "type": "object" + }, + { + "description": "Behavior", + "name": "Behavior", + "supplementaryInformation": "ShakeObject::ShakeObject_PositionAngleScale", + "type": "behavior" + }, + { + "description": "Duration of shake (in seconds) (Default: 0.5)", + "name": "Duration", + "type": "expression" + }, + { + "description": "Amplitude of postion shake in X direction (in pixels) (For example: 5)", + "name": "PowerX", + "type": "expression" + }, + { + "description": "Amplitude of position shake in Y direction (in pixels) (For example: 5)", + "longDescription": "Use a negative number to make the single-shake move in the opposite direction.", + "name": "PowerY", + "type": "expression" + }, + { + "description": "Amplitude of angle rotation shake (in degrees) (For example: 5)", + "name": "PowerAngle", + "type": "expression" + }, + { + "description": "Amplitude of scale shake (in percent change) (For example: 5)", + "name": "PowerScale", + "type": "expression" + }, + { + "description": "Amount of time between shakes (in seconds) (Default: 0.08)", + "longDescription": "For a single-shake effect, set it to the same value as \"Duration\".", + "name": "TimeBetweenShakes", + "type": "expression" + }, + { + "description": "Keep shaking until stopped", + "longDescription": "Duration value will be ignored", + "name": "ShakeForever", + "type": "yesorno" + } + ], + "objectGroups": [] + }, + { + "fullName": "", + "functionType": "Action", + "name": "doStepPostEvents", + "sentence": "", + "events": [ + { + "colorB": 228, + "colorG": 176, + "colorR": 74, + "creationTime": 0, + "name": "Shake Object", + "source": "", + "type": "BuiltinCommonInstructions::Group", + "events": [ + { + "colorB": 228, + "colorG": 176, + "colorR": 74, + "creationTime": 0, + "name": "Start shaking", + "source": "", + "type": "BuiltinCommonInstructions::Group", + "events": [ + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [ + { + "type": { + "value": "VarObjet" + }, + "parameters": [ + "Object", + "__ShakeObject_ShakeInProgress", + "=", + "1" + ] + } + ], + "actions": [], + "events": [ + { + "type": "BuiltinCommonInstructions::Comment", + "color": { + "b": 109, + "g": 230, + "r": 255, + "textB": 0, + "textG": 0, + "textR": 0 + }, + "comment": "Keep object shaking forever (if desired)" + }, + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [ + { + "type": { + "value": "ObjectVariableAsBoolean" + }, + "parameters": [ + "Object", + "__ShakeObject_ShakeForever", + "True" + ] + } + ], + "actions": [ + { + "type": { + "value": "ModVarObjet" + }, + "parameters": [ + "Object", + "__ShakeObject_Duration", + "=", + "100" + ] + }, + { + "type": { + "value": "ResetObjectTimer" + }, + "parameters": [ + "Object", + "\"__ShakeObject_DurationTimer\"" + ] + } + ] + }, + { + "colorB": 228, + "colorG": 176, + "colorR": 74, + "creationTime": 0, + "name": "Calculate movement of the shake", + "source": "", + "type": "BuiltinCommonInstructions::Group", + "events": [ + { + "type": "BuiltinCommonInstructions::Comment", + "color": { + "b": 109, + "g": 230, + "r": 255, + "textB": 0, + "textG": 0, + "textR": 0 + }, + "comment": "Run once before every shake movement" + }, + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [ + { + "type": { + "value": "BuiltinCommonInstructions::Or" + }, + "parameters": [], + "subInstructions": [ + { + "type": { + "value": "VarObjet" + }, + "parameters": [ + "Object", + "__ShakeObject_InitialShake", + "=", + "1" + ] + }, + { + "type": { + "value": "ObjectTimer" + }, + "parameters": [ + "Object", + "\"__ShakeObject_ShakeTimer\"", + "Object.Variable(__ShakeObject_TimeBetweenShakes)" + ] + } + ] + } + ], + "actions": [ + { + "type": { + "value": "ResetObjectTimer" + }, + "parameters": [ + "Object", + "\"__ShakeObject_ShakeTimer\"" + ] + }, + { + "type": { + "value": "ModVarObjet" + }, + "parameters": [ + "Object", + "__ShakeObject_ShakeCounter", + "+", + "1" + ] + } + ], + "events": [ + { + "colorB": 228, + "colorG": 176, + "colorR": 74, + "creationTime": 0, + "folded": true, + "name": "Correct for drift and reset drift tracking variables", + "source": "", + "type": "BuiltinCommonInstructions::Group", + "events": [ + { + "type": "BuiltinCommonInstructions::Comment", + "color": { + "b": 109, + "g": 230, + "r": 255, + "textB": 0, + "textG": 0, + "textR": 0 + }, + "comment": "Move to correct any drift from previous shake" + }, + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [ + { + "type": { + "value": "BuiltinCommonInstructions::Or" + }, + "parameters": [], + "subInstructions": [ + { + "type": { + "value": "VarObjet" + }, + "parameters": [ + "Object", + "__ShakeObject_PowerX", + "!=", + "0" + ] + }, + { + "type": { + "value": "VarObjet" + }, + "parameters": [ + "Object", + "__ShakeObject_PowerY", + "!=", + "0" + ] + } + ] + } + ], + "actions": [ + { + "type": { + "value": "SetX" + }, + "parameters": [ + "Object", + "-", + "Object.Variable(__ShakeObject_DisplacementTravelledX)" + ] + }, + { + "type": { + "value": "SetY" + }, + "parameters": [ + "Object", + "-", + "Object.Variable(__ShakeObject_DisplacementTravelledY)" + ] + } + ] + }, + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [ + { + "type": { + "value": "VarObjet" + }, + "parameters": [ + "Object", + "__ShakeObject_PowerAngle", + "!=", + "0" + ] + } + ], + "actions": [ + { + "type": { + "value": "SetAngle" + }, + "parameters": [ + "Object", + "-", + "Object.Variable(__ShakeObject_AngleTravelled)" + ] + } + ] + }, + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [ + { + "type": { + "value": "VarObjet" + }, + "parameters": [ + "Object", + "__ShakeObject_PowerScale", + "!=", + "0" + ] + } + ], + "actions": [ + { + "type": { + "value": "ScalableCapability::ScalableBehavior::SetValue" + }, + "parameters": [ + "Object", + "Scale", + "-", + "Object.Variable(__ShakeObject_ScaleTravelled)" + ] + } + ] + }, + { + "type": "BuiltinCommonInstructions::Comment", + "color": { + "b": 109, + "g": 230, + "r": 255, + "textB": 0, + "textG": 0, + "textR": 0 + }, + "comment": "Reset drift detection variables" + }, + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [], + "actions": [ + { + "type": { + "value": "ModVarObjet" + }, + "parameters": [ + "Object", + "__ShakeObject_DisplacementTravelledX", + "=", + "0" + ] + }, + { + "type": { + "value": "ModVarObjet" + }, + "parameters": [ + "Object", + "__ShakeObject_DisplacementTravelledY", + "=", + "0" + ] + }, + { + "type": { + "value": "ModVarObjet" + }, + "parameters": [ + "Object", + "__ShakeObject_AngleTravelled", + "=", + "0" + ] + }, + { + "type": { + "value": "ModVarObjet" + }, + "parameters": [ + "Object", + "__ShakeObject_ScaleTravelled", + "=", + "0" + ] + } + ] + } + ], + "parameters": [] + }, + { + "type": "BuiltinCommonInstructions::Comment", + "color": { + "b": 109, + "g": 230, + "r": 255, + "textB": 0, + "textG": 0, + "textR": 0 + }, + "comment": "Calculate Position Shake" + }, + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [ + { + "type": { + "value": "BuiltinCommonInstructions::Or" + }, + "parameters": [], + "subInstructions": [ + { + "type": { + "value": "VarObjet" + }, + "parameters": [ + "Object", + "__ShakeObject_PowerX", + "!=", + "0" + ] + }, + { + "type": { + "value": "VarObjet" + }, + "parameters": [ + "Object", + "__ShakeObject_PowerY", + "!=", + "0" + ] + } + ] + } + ], + "actions": [], + "events": [ + { + "type": "BuiltinCommonInstructions::Comment", + "color": { + "b": 109, + "g": 230, + "r": 255, + "textB": 0, + "textG": 0, + "textR": 0 + }, + "comment": "PositionDisplacement = (DesiredDuration - RunningTimer) / DesiredDuration * Amplitude * [-1 or 1]" + }, + { + "type": "BuiltinCommonInstructions::Comment", + "color": { + "b": 109, + "g": 230, + "r": 255, + "textB": 0, + "textG": 0, + "textR": 0 + }, + "comment": "Make initial shake NOT random so users can set a direction for a one-shake effect" + }, + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [ + { + "type": { + "value": "VarObjet" + }, + "parameters": [ + "Object", + "__ShakeObject_InitialShake", + "=", + "1" + ] + } + ], + "actions": [], + "events": [ + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [ + { + "type": { + "value": "VarObjet" + }, + "parameters": [ + "Object", + "__ShakeObject_PowerX", + "!=", + "0" + ] + } + ], + "actions": [ + { + "type": { + "value": "ModVarObjet" + }, + "parameters": [ + "Object", + "__ShakeObject_DisplacementX", + "=", + "(Object.Variable(__ShakeObject_Duration) - Object.ObjectTimerElapsedTime(\"__ShakeObject_DurationTimer\")) / Object.Variable(__ShakeObject_Duration) * Object.Variable(__ShakeObject_PowerX)" + ] + } + ] + }, + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [ + { + "type": { + "value": "VarObjet" + }, + "parameters": [ + "Object", + "__ShakeObject_PowerY", + "!=", + "0" + ] + } + ], + "actions": [ + { + "type": { + "value": "ModVarObjet" + }, + "parameters": [ + "Object", + "__ShakeObject_DisplacementY", + "=", + "(Object.Variable(__ShakeObject_Duration) - Object.ObjectTimerElapsedTime(\"__ShakeObject_DurationTimer\")) / Object.Variable(__ShakeObject_Duration) * Object.Variable(__ShakeObject_PowerY)" + ] + } + ] + } + ] + }, + { + "type": "BuiltinCommonInstructions::Comment", + "color": { + "b": 109, + "g": 230, + "r": 255, + "textB": 0, + "textG": 0, + "textR": 0 + }, + "comment": "After initial shake pick a random direction" + }, + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [ + { + "type": { + "value": "VarObjet" + }, + "parameters": [ + "Object", + "__ShakeObject_InitialShake", + "=", + "0" + ] + } + ], + "actions": [], + "events": [ + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [ + { + "type": { + "value": "VarObjet" + }, + "parameters": [ + "Object", + "__ShakeObject_PowerX", + "!=", + "0" + ] + } + ], + "actions": [ + { + "type": { + "value": "ModVarObjet" + }, + "parameters": [ + "Object", + "__ShakeObject_DisplacementX", + "=", + "(Object.Variable(__ShakeObject_Duration) - Object.ObjectTimerElapsedTime(\"__ShakeObject_DurationTimer\")) / Object.Variable(__ShakeObject_Duration) * Object.Variable(__ShakeObject_PowerX) * RandomWithStep(-1, 1, 2)" + ] + } + ] + }, + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [ + { + "type": { + "value": "VarObjet" + }, + "parameters": [ + "Object", + "__ShakeObject_PowerY", + "!=", + "0" + ] + } + ], + "actions": [ + { + "type": { + "value": "ModVarObjet" + }, + "parameters": [ + "Object", + "__ShakeObject_DisplacementY", + "=", + "(Object.Variable(__ShakeObject_Duration) - Object.ObjectTimerElapsedTime(\"__ShakeObject_DurationTimer\")) / Object.Variable(__ShakeObject_Duration) * Object.Variable(__ShakeObject_PowerY) * RandomWithStep(-1, 1, 2)" + ] + } + ] + } + ] + } + ] + }, + { + "type": "BuiltinCommonInstructions::Comment", + "color": { + "b": 109, + "g": 230, + "r": 255, + "textB": 0, + "textG": 0, + "textR": 0 + }, + "comment": "Calculate Rotation (angle) shake " + }, + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [ + { + "type": { + "value": "VarObjet" + }, + "parameters": [ + "Object", + "__ShakeObject_PowerAngle", + "!=", + "0" + ] + } + ], + "actions": [], + "events": [ + { + "type": "BuiltinCommonInstructions::Comment", + "color": { + "b": 109, + "g": 230, + "r": 255, + "textB": 0, + "textG": 0, + "textR": 0 + }, + "comment": "Every \"even\" shake, rotate clockwise" + }, + { + "type": "BuiltinCommonInstructions::Comment", + "color": { + "b": 109, + "g": 230, + "r": 255, + "textB": 0, + "textG": 0, + "textR": 0 + }, + "comment": "AngleDisplacement = (DesiredDuration - RunningTimer) / DesiredDuration * Amplitude" + }, + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [ + { + "type": { + "value": "BuiltinCommonInstructions::CompareNumbers" + }, + "parameters": [ + "mod(Object.Variable(__ShakeObject_ShakeCounter),2)", + "=", + "0" + ] + } + ], + "actions": [ + { + "type": { + "value": "ModVarObjet" + }, + "parameters": [ + "Object", + "__ShakeObject_DisplacementAngle", + "=", + "-1 * (Object.Variable(__ShakeObject_Duration) - Object.ObjectTimerElapsedTime(\"__ShakeObject_DurationTimer\")) / Object.Variable(__ShakeObject_Duration) * Object.Variable(__ShakeObject_PowerAngle)" + ] + } + ] + }, + { + "type": "BuiltinCommonInstructions::Comment", + "color": { + "b": 109, + "g": 230, + "r": 255, + "textB": 0, + "textG": 0, + "textR": 0 + }, + "comment": "Every \"odd\" shake, rotate counter-clockwise" + }, + { + "type": "BuiltinCommonInstructions::Comment", + "color": { + "b": 109, + "g": 230, + "r": 255, + "textB": 0, + "textG": 0, + "textR": 0 + }, + "comment": "AngleDisplacement = (DesiredDuration - RunningTimer) / DesiredDuration * Amplitude" + }, + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [ + { + "type": { + "value": "BuiltinCommonInstructions::CompareNumbers" + }, + "parameters": [ + "mod(Object.Variable(__ShakeObject_ShakeCounter),2)", + "=", + "1" + ] + } + ], + "actions": [ + { + "type": { + "value": "ModVarObjet" + }, + "parameters": [ + "Object", + "__ShakeObject_DisplacementAngle", + "=", + "(Object.Variable(__ShakeObject_Duration) - Object.ObjectTimerElapsedTime(\"__ShakeObject_DurationTimer\")) / Object.Variable(__ShakeObject_Duration) * Object.Variable(__ShakeObject_PowerAngle)" + ] + } + ] + } + ] + }, + { + "type": "BuiltinCommonInstructions::Comment", + "color": { + "b": 109, + "g": 230, + "r": 255, + "textB": 0, + "textG": 0, + "textR": 0 + }, + "comment": "Calculate Scale shake" + }, + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [ + { + "type": { + "value": "VarObjet" + }, + "parameters": [ + "Object", + "__ShakeObject_PowerScale", + "!=", + "0" + ] + } + ], + "actions": [], + "events": [ + { + "type": "BuiltinCommonInstructions::Comment", + "color": { + "b": 109, + "g": 230, + "r": 255, + "textB": 0, + "textG": 0, + "textR": 0 + }, + "comment": "Calculate object scale displacement, with linear decay over time" + }, + { + "type": "BuiltinCommonInstructions::Comment", + "color": { + "b": 109, + "g": 230, + "r": 255, + "textB": 0, + "textG": 0, + "textR": 0 + }, + "comment": "ScaleDisplacement = (DesiredDuration - RunningTimer) / DesiredDuration * Amplitude * 1/100" + }, + { + "type": "BuiltinCommonInstructions::Comment", + "color": { + "b": 109, + "g": 230, + "r": 255, + "textB": 0, + "textG": 0, + "textR": 0 + }, + "comment": "Every even shake, increase scale" + }, + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [ + { + "type": { + "value": "BuiltinCommonInstructions::CompareNumbers" + }, + "parameters": [ + "mod(Object.Variable(__ShakeObject_ShakeCounter),2)", + "=", + "0" + ] + } + ], + "actions": [ + { + "type": { + "value": "ModVarObjet" + }, + "parameters": [ + "Object", + "__ShakeObject_DisplacementScale", + "=", + "-1 * (Object.Variable(__ShakeObject_Duration) - Object.ObjectTimerElapsedTime(\"__ShakeObject_DurationTimer\")) / Object.Variable(__ShakeObject_Duration) * Object.Variable(__ShakeObject_PowerScale) * (1/100)" + ] + } + ] + }, + { + "type": "BuiltinCommonInstructions::Comment", + "color": { + "b": 109, + "g": 230, + "r": 255, + "textB": 0, + "textG": 0, + "textR": 0 + }, + "comment": "Every \"odd\" shake, decrease scale" + }, + { + "type": "BuiltinCommonInstructions::Comment", + "color": { + "b": 109, + "g": 230, + "r": 255, + "textB": 0, + "textG": 0, + "textR": 0 + }, + "comment": "ScaleDisplacement = -1 * (DesiredDuration - RunningTimer) / DesiredDuration * Amplitude * 1/100" + }, + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [ + { + "type": { + "value": "BuiltinCommonInstructions::CompareNumbers" + }, + "parameters": [ + "mod(Object.Variable(__ShakeObject_ShakeCounter),2)", + "=", + "1" + ] + } + ], + "actions": [ + { + "type": { + "value": "ModVarObjet" + }, + "parameters": [ + "Object", + "__ShakeObject_DisplacementScale", + "=", + "(Object.Variable(__ShakeObject_Duration) - Object.ObjectTimerElapsedTime(\"__ShakeObject_DurationTimer\")) / Object.Variable(__ShakeObject_Duration) * Object.Variable(__ShakeObject_PowerScale) * (1/100)" + ] + } + ] + } + ] + }, + { + "type": "BuiltinCommonInstructions::Comment", + "color": { + "b": 109, + "g": 230, + "r": 255, + "textB": 0, + "textG": 0, + "textR": 0 + }, + "comment": "Save that initial shake has been processed" + }, + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [], + "actions": [ + { + "type": { + "value": "ModVarObjet" + }, + "parameters": [ + "Object", + "__ShakeObject_InitialShake", + "=", + "0" + ] + } + ] + } + ] + } + ], + "parameters": [] + }, + { + "colorB": 228, + "colorG": 176, + "colorR": 74, + "creationTime": 0, + "name": "Move object", + "source": "", + "type": "BuiltinCommonInstructions::Group", + "events": [ + { + "type": "BuiltinCommonInstructions::Comment", + "color": { + "b": 109, + "g": 230, + "r": 255, + "textB": 0, + "textG": 0, + "textR": 0 + }, + "comment": "Calculate the fraction of shake that occured during this frame" + }, + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [], + "actions": [ + { + "type": { + "value": "ModVarObjet" + }, + "parameters": [ + "Object", + "__ShakeObject_PercentTimeElapsedThisFrame", + "=", + "min(1,TimeDelta()/Object.Variable(__ShakeObject_TimeBetweenShakes))" + ] + } + ] + }, + { + "type": "BuiltinCommonInstructions::Comment", + "color": { + "b": 109, + "g": 230, + "r": 255, + "textB": 0, + "textG": 0, + "textR": 0 + }, + "comment": "Increase change for the first half of the shake (move away from original values)" + }, + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [ + { + "type": { + "inverted": true, + "value": "ObjectTimer" + }, + "parameters": [ + "Object", + "\"__ShakeObject_ShakeTimer\"", + "Object.Variable(__ShakeObject_TimeBetweenShakes)/2" + ] + } + ], + "actions": [], + "events": [ + { + "type": "BuiltinCommonInstructions::Comment", + "color": { + "b": 109, + "g": 230, + "r": 255, + "textB": 0, + "textG": 0, + "textR": 0 + }, + "comment": "Change position" + }, + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [ + { + "type": { + "value": "BuiltinCommonInstructions::Or" + }, + "parameters": [], + "subInstructions": [ + { + "type": { + "value": "VarObjet" + }, + "parameters": [ + "Object", + "__ShakeObject_PowerX", + "!=", + "0" + ] + }, + { + "type": { + "value": "VarObjet" + }, + "parameters": [ + "Object", + "__ShakeObject_PowerY", + "!=", + "0" + ] + } + ] + } + ], + "actions": [ + { + "type": { + "value": "SetX" + }, + "parameters": [ + "Object", + "+", + "round(1024 * Object.Variable(__ShakeObject_DisplacementX) * Object.Variable(__ShakeObject_PercentTimeElapsedThisFrame)) / 1024" + ] + }, + { + "type": { + "value": "SetY" + }, + "parameters": [ + "Object", + "+", + "round(1024 * Object.Variable(__ShakeObject_DisplacementY) * Object.Variable(__ShakeObject_PercentTimeElapsedThisFrame)) / 1024" + ] + } + ], + "events": [ + { + "type": "BuiltinCommonInstructions::Comment", + "color": { + "b": 109, + "g": 230, + "r": 255, + "textB": 0, + "textG": 0, + "textR": 0 + }, + "comment": "Save movement to calculate drift" + }, + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [], + "actions": [ + { + "type": { + "value": "ModVarObjet" + }, + "parameters": [ + "Object", + "__ShakeObject_DisplacementTravelledX", + "+", + "round(1024 * Object.Variable(__ShakeObject_DisplacementX) * Object.Variable(__ShakeObject_PercentTimeElapsedThisFrame)) / 1024" + ] + }, + { + "type": { + "value": "ModVarObjet" + }, + "parameters": [ + "Object", + "__ShakeObject_DisplacementTravelledY", + "+", + "round(1024 * Object.Variable(__ShakeObject_DisplacementY) * Object.Variable(__ShakeObject_PercentTimeElapsedThisFrame)) / 1024" + ] + } + ] + } + ] + }, + { + "type": "BuiltinCommonInstructions::Comment", + "color": { + "b": 109, + "g": 230, + "r": 255, + "textB": 0, + "textG": 0, + "textR": 0 + }, + "comment": "Change angle" + }, + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [ + { + "type": { + "value": "BuiltinCommonInstructions::CompareNumbers" + }, + "parameters": [ + "Object.Variable(__ShakeObject_PowerAngle)", + "!=", + "0" + ] + } + ], + "actions": [ + { + "type": { + "value": "SetAngle" + }, + "parameters": [ + "Object", + "+", + "round(1024 * Object.Variable(__ShakeObject_DisplacementAngle) * Object.Variable(__ShakeObject_PercentTimeElapsedThisFrame)) / 1024" + ] + } + ], + "events": [ + { + "type": "BuiltinCommonInstructions::Comment", + "color": { + "b": 109, + "g": 230, + "r": 255, + "textB": 0, + "textG": 0, + "textR": 0 + }, + "comment": "Save movement to calculate drift" + }, + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [], + "actions": [ + { + "type": { + "value": "ModVarObjet" + }, + "parameters": [ + "Object", + "__ShakeObject_AngleTravelled", + "+", + "round(1024 * Object.Variable(__ShakeObject_DisplacementAngle) * Object.Variable(__ShakeObject_PercentTimeElapsedThisFrame)) / 1024" + ] + } + ] + } + ] + }, + { + "type": "BuiltinCommonInstructions::Comment", + "color": { + "b": 109, + "g": 230, + "r": 255, + "textB": 0, + "textG": 0, + "textR": 0 + }, + "comment": "Change scale" + }, + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [ + { + "type": { + "value": "BuiltinCommonInstructions::CompareNumbers" + }, + "parameters": [ + "Object.Variable(__ShakeObject_PowerScale)", + "!=", + "0" + ] + } + ], + "actions": [ + { + "type": { + "value": "ScalableCapability::ScalableBehavior::SetValue" + }, + "parameters": [ + "Object", + "Scale", + "+", + "round(1024 * Object.Variable(__ShakeObject_DisplacementScale) * Object.Variable(__ShakeObject_PercentTimeElapsedThisFrame)) / 1024" + ] + } + ], + "events": [ + { + "type": "BuiltinCommonInstructions::Comment", + "color": { + "b": 109, + "g": 230, + "r": 255, + "textB": 0, + "textG": 0, + "textR": 0 + }, + "comment": "Save movement to calculate drift" + }, + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [], + "actions": [ + { + "type": { + "value": "ModVarObjet" + }, + "parameters": [ + "Object", + "__ShakeObject_ScaleTravelled", + "+", + "round(1024 * Object.Variable(__ShakeObject_DisplacementScale) * Object.Variable(__ShakeObject_PercentTimeElapsedThisFrame)) / 1024" + ] + } + ] + } + ] + } + ] + }, + { + "type": "BuiltinCommonInstructions::Comment", + "color": { + "b": 109, + "g": 230, + "r": 255, + "textB": 0, + "textG": 0, + "textR": 0 + }, + "comment": "Decrease change the second half of the shake (return to original position)" + }, + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [ + { + "type": { + "value": "ObjectTimer" + }, + "parameters": [ + "Object", + "\"__ShakeObject_ShakeTimer\"", + "Object.Variable(__ShakeObject_TimeBetweenShakes)/2" + ] + } + ], + "actions": [], + "events": [ + { + "type": "BuiltinCommonInstructions::Comment", + "color": { + "b": 109, + "g": 230, + "r": 255, + "textB": 0, + "textG": 0, + "textR": 0 + }, + "comment": "Change position" + }, + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [ + { + "type": { + "value": "BuiltinCommonInstructions::Or" + }, + "parameters": [], + "subInstructions": [ + { + "type": { + "value": "VarObjet" + }, + "parameters": [ + "Object", + "__ShakeObject_PowerX", + "!=", + "0" + ] + }, + { + "type": { + "value": "VarObjet" + }, + "parameters": [ + "Object", + "__ShakeObject_PowerY", + "!=", + "0" + ] + } + ] + } + ], + "actions": [ + { + "type": { + "value": "SetX" + }, + "parameters": [ + "Object", + "-", + "round(1024 * Object.Variable(__ShakeObject_DisplacementX) * Object.Variable(__ShakeObject_PercentTimeElapsedThisFrame)) / 1024" + ] + }, + { + "type": { + "value": "SetY" + }, + "parameters": [ + "Object", + "-", + "round(1024 * Object.Variable(__ShakeObject_DisplacementY) * Object.Variable(__ShakeObject_PercentTimeElapsedThisFrame)) / 1024" + ] + } + ], + "events": [ + { + "type": "BuiltinCommonInstructions::Comment", + "color": { + "b": 109, + "g": 230, + "r": 255, + "textB": 0, + "textG": 0, + "textR": 0 + }, + "comment": "Save movement to calculate drift" + }, + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [], + "actions": [ + { + "type": { + "value": "ModVarObjet" + }, + "parameters": [ + "Object", + "__ShakeObject_DisplacementTravelledX", + "-", + "round(1024 * Object.Variable(__ShakeObject_DisplacementX) * Object.Variable(__ShakeObject_PercentTimeElapsedThisFrame)) / 1024" + ] + }, + { + "type": { + "value": "ModVarObjet" + }, + "parameters": [ + "Object", + "__ShakeObject_DisplacementTravelledY", + "-", + "round(1024 * Object.Variable(__ShakeObject_DisplacementY) * Object.Variable(__ShakeObject_PercentTimeElapsedThisFrame)) / 1024" + ] + } + ] + } + ] + }, + { + "type": "BuiltinCommonInstructions::Comment", + "color": { + "b": 109, + "g": 230, + "r": 255, + "textB": 0, + "textG": 0, + "textR": 0 + }, + "comment": "Change angle" + }, + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [ + { + "type": { + "value": "BuiltinCommonInstructions::CompareNumbers" + }, + "parameters": [ + "Object.Variable(__ShakeObject_PowerAngle)", + "!=", + "0" + ] + } + ], + "actions": [ + { + "type": { + "value": "SetAngle" + }, + "parameters": [ + "Object", + "-", + "round(1024 * Object.Variable(__ShakeObject_DisplacementAngle) * Object.Variable(__ShakeObject_PercentTimeElapsedThisFrame)) / 1024" + ] + } + ], + "events": [ + { + "type": "BuiltinCommonInstructions::Comment", + "color": { + "b": 109, + "g": 230, + "r": 255, + "textB": 0, + "textG": 0, + "textR": 0 + }, + "comment": "Save movement to calculate drift" + }, + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [], + "actions": [ + { + "type": { + "value": "ModVarObjet" + }, + "parameters": [ + "Object", + "__ShakeObject_AngleTravelled", + "-", + "round(1024 * Object.Variable(__ShakeObject_DisplacementAngle) * Object.Variable(__ShakeObject_PercentTimeElapsedThisFrame)) / 1024" + ] + } + ] + } + ] + }, + { + "type": "BuiltinCommonInstructions::Comment", + "color": { + "b": 109, + "g": 230, + "r": 255, + "textB": 0, + "textG": 0, + "textR": 0 + }, + "comment": "Change scale" + }, + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [ + { + "type": { + "value": "BuiltinCommonInstructions::CompareNumbers" + }, + "parameters": [ + "Object.Variable(__ShakeObject_PowerScale)", + "!=", + "0" + ] + } + ], + "actions": [ + { + "type": { + "value": "ScalableCapability::ScalableBehavior::SetValue" + }, + "parameters": [ + "Object", + "Scale", + "-", + "round(1024 * Object.Variable(__ShakeObject_DisplacementScale) * Object.Variable(__ShakeObject_PercentTimeElapsedThisFrame)) / 1024" + ] + } + ], + "events": [ + { + "type": "BuiltinCommonInstructions::Comment", + "color": { + "b": 109, + "g": 230, + "r": 255, + "textB": 0, + "textG": 0, + "textR": 0 + }, + "comment": "Save movement to calculate drift" + }, + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [], + "actions": [ + { + "type": { + "value": "ModVarObjet" + }, + "parameters": [ + "Object", + "__ShakeObject_ScaleTravelled", + "-", + "round(1024 * Object.Variable(__ShakeObject_DisplacementScale) * Object.Variable(__ShakeObject_PercentTimeElapsedThisFrame)) / 1024" + ] + } + ] + } + ] + } + ] + } + ], + "parameters": [] + } + ] + } + ], + "parameters": [] + }, + { + "colorB": 228, + "colorG": 176, + "colorR": 74, + "creationTime": 0, + "name": "Stop shaking", + "source": "", + "type": "BuiltinCommonInstructions::Group", + "events": [ + { + "type": "BuiltinCommonInstructions::Comment", + "color": { + "b": 109, + "g": 230, + "r": 255, + "textB": 0, + "textG": 0, + "textR": 0 + }, + "comment": "Stop shaking when the duration has been reached (or if the stop shaking function was called)" + }, + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [ + { + "type": { + "value": "BuiltinCommonInstructions::Or" + }, + "parameters": [], + "subInstructions": [ + { + "type": { + "value": "ObjectTimer" + }, + "parameters": [ + "Object", + "\"__ShakeObject_DurationTimer\"", + "Object.Variable(__ShakeObject_Duration)" + ] + }, + { + "type": { + "value": "VarObjet" + }, + "parameters": [ + "Object", + "__ShakeObject_ShakeInProgress", + "=", + "-1" + ] + } + ] + }, + { + "type": { + "value": "BuiltinCommonInstructions::Once" + }, + "parameters": [] + } + ], + "actions": [ + { + "type": { + "value": "ModVarObjet" + }, + "parameters": [ + "Object", + "__ShakeObject_ShakeInProgress", + "=", + "0" + ] + }, + { + "type": { + "value": "ModVarObjet" + }, + "parameters": [ + "Object", + "__ShakeObject_ShakeCounter", + "=", + "0" + ] + } + ], + "events": [ + { + "colorB": 228, + "colorG": 176, + "colorR": 74, + "creationTime": 0, + "name": "Correct for drift and reset drift tracking variables", + "source": "", + "type": "BuiltinCommonInstructions::Group", + "events": [ + { + "type": "BuiltinCommonInstructions::Comment", + "color": { + "b": 109, + "g": 230, + "r": 255, + "textB": 0, + "textG": 0, + "textR": 0 + }, + "comment": "Move to correct any drift from previous shake" + }, + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [ + { + "type": { + "value": "BuiltinCommonInstructions::Or" + }, + "parameters": [], + "subInstructions": [ + { + "type": { + "value": "VarObjet" + }, + "parameters": [ + "Object", + "__ShakeObject_PowerX", + "!=", + "0" + ] + }, + { + "type": { + "value": "VarObjet" + }, + "parameters": [ + "Object", + "__ShakeObject_PowerY", + "!=", + "0" + ] + } + ] + } + ], + "actions": [ + { + "type": { + "value": "SetX" + }, + "parameters": [ + "Object", + "-", + "Object.Variable(__ShakeObject_DisplacementTravelledX)" + ] + }, + { + "type": { + "value": "SetY" + }, + "parameters": [ + "Object", + "-", + "Object.Variable(__ShakeObject_DisplacementTravelledY)" + ] + } + ] + }, + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [ + { + "type": { + "value": "VarObjet" + }, + "parameters": [ + "Object", + "__ShakeObject_PowerAngle", + "!=", + "0" + ] + } + ], + "actions": [ + { + "type": { + "value": "SetAngle" + }, + "parameters": [ + "Object", + "-", + "Object.Variable(__ShakeObject_AngleTravelled)" + ] + } + ] + }, + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [ + { + "type": { + "value": "VarObjet" + }, + "parameters": [ + "Object", + "__ShakeObject_PowerScale", + "!=", + "0" + ] + } + ], + "actions": [ + { + "type": { + "value": "ScalableCapability::ScalableBehavior::SetValue" + }, + "parameters": [ + "Object", + "Scale", + "-", + "Object.Variable(__ShakeObject_ScaleTravelled)" + ] + } + ] + }, + { + "type": "BuiltinCommonInstructions::Comment", + "color": { + "b": 109, + "g": 230, + "r": 255, + "textB": 0, + "textG": 0, + "textR": 0 + }, + "comment": "Reset drift detection variables" + }, + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [], + "actions": [ + { + "type": { + "value": "ModVarObjet" + }, + "parameters": [ + "Object", + "__ShakeObject_DisplacementTravelledX", + "=", + "0" + ] + }, + { + "type": { + "value": "ModVarObjet" + }, + "parameters": [ + "Object", + "__ShakeObject_DisplacementTravelledY", + "=", + "0" + ] + }, + { + "type": { + "value": "ModVarObjet" + }, + "parameters": [ + "Object", + "__ShakeObject_AngleTravelled", + "=", + "0" + ] + }, + { + "type": { + "value": "ModVarObjet" + }, + "parameters": [ + "Object", + "__ShakeObject_ScaleTravelled", + "=", + "0" + ] + } + ] + } + ], + "parameters": [] + } + ] + } + ], + "parameters": [] + } + ], + "parameters": [] + } + ], + "parameters": [ + { + "description": "Object", + "name": "Object", + "type": "object" + }, + { + "description": "Behavior", + "name": "Behavior", + "supplementaryInformation": "ShakeObject::ShakeObject_PositionAngleScale", + "type": "behavior" + } + ], + "objectGroups": [] + }, + { + "description": "Stop shaking an object.", + "fullName": "Stop shaking an object", + "functionType": "Action", + "name": "StopShaking", + "sentence": "Stop shaking _PARAM0_", + "events": [ + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [ + { + "type": { + "value": "ShakeObject::ShakeObject_PositionAngleScale::IsShaking" + }, + "parameters": [ + "Object", + "Behavior", + "" + ] + } + ], + "actions": [ + { + "type": { + "value": "ModVarObjet" + }, + "parameters": [ + "Object", + "__ShakeObject_ShakeInProgress", + "=", + "-1" + ] + } + ] + } + ], + "parameters": [ + { + "description": "Object", + "name": "Object", + "type": "object" + }, + { + "description": "Behavior", + "name": "Behavior", + "supplementaryInformation": "ShakeObject::ShakeObject_PositionAngleScale", + "type": "behavior" + } + ], + "objectGroups": [] + }, + { + "description": "Check if an object is shaking.", + "fullName": "Check if an object is shaking", + "functionType": "Condition", + "name": "IsShaking", + "sentence": "_PARAM0_ is shaking", + "events": [ + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [ + { + "type": { + "value": "VarObjet" + }, + "parameters": [ + "Object", + "__ShakeObject_ShakeInProgress", + "=", + "1" + ] + } + ], + "actions": [ + { + "type": { + "value": "SetReturnBoolean" + }, + "parameters": [ + "True" + ] + } + ] + } + ], + "parameters": [ + { + "description": "Object", + "name": "Object", + "type": "object" + }, + { + "description": "Behavior", + "name": "Behavior", + "supplementaryInformation": "ShakeObject::ShakeObject_PositionAngleScale", + "type": "behavior" + } + ], + "objectGroups": [] + } + ], + "eventsFunctionsFolderStructure": { + "folderName": "__ROOT", + "children": [ + { + "functionName": "ShakeObject_PositionAngleScale" + }, + { + "functionName": "doStepPostEvents" + }, + { + "functionName": "StopShaking" + }, + { + "functionName": "IsShaking" + } + ] + }, + "propertyDescriptors": [ + { + "value": "", + "type": "Behavior", + "label": "Scalable capability", + "extraInformation": [ + "ScalableCapability::ScalableBehavior" + ], + "choices": [], + "name": "Scale" + } + ], + "propertiesFolderStructure": { + "folderName": "__ROOT", + "children": [ + { + "propertyName": "Scale" + } + ] + } + } + ], + "eventsBasedObjects": [] + } + ], "externalLayouts": [] } diff --git a/examples/starting-physics/assets/Background.png b/examples/starting-physics/assets/Background.png index 83470d7ee7b6b56c474728e849f9b61fc8860c4b..313afed01e19a3496c7e7147c0c31af83fba1bd8 100644 GIT binary patch literal 381 zcmeAS@N?(olHy`uVBq!ia0vp^4Is?H1|$#LC7uRSoCO|{#S9GG!XV7ZFl&wkP>``W z$lZxy-8q?;Kn_c~qpu?a!^VE@KZ&eBK3|DzL`iUdT1k0gQ7VI5W_oVoyp7Y6figcl zT^vIy7~kI6D9Y$4!g|48%RxzDfsv_(<5CuFg$St)+0OzFpLti9Z}fZdcHTYHcU9Xj z>v&zZ+0MoF?&A7m5~n}u?vMSaw0!@)!v%e(+P)W@Iv-m1xCUr40wl!cegAjKcHZ$7 zYf`4Qt@^X==gKRGKmWazdu{a!jpuQnHvgadN&2b&sfnNXU2A@lJ`mkqwgfboFyt=akR{00FX$VgLXD delta 181 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 delta 628 zcmV-)0*n2L6oLhi8Gi!+005o0f$RVP04h*SR7Ff_aqEi#;DrL^hymq@0po`PPbXFRCwC0SBsLuAPgko(E$4YzkhoatCkQbskhFk#q5$t zrHL^E1nWEk#0Y@i*`4Jq%A;4Wq*a^K#*m>+2FM-?r?XR40 zaS=}D`?w7MBVSZd@dX7Jm0)?WQH`b+8|Ve#d_Q2k0sJX|CWrv{1R&h40IS4Z2R<&? zcpFwM!LvZJ7Jt@jAvR|A*wHJz)*ozj>r!~w^$N%OqfaA>0$gFRUSbIHMqFUD9$qy7 zxGP{w3LsNKhN&SBVJGbrXFSjWXbj^_;LGs_i6n*Rpo}e_2+smvg{OEQm37W%0%`3A z&W!*Ocj59c)KLCeAZsHi0@z%x2s8v_0-%imjc=|O>VJ?Lf;WzIF0>AiGnRB|Y*R?q zgpc=2EZfXL@~w>i}X1|A0F-UdI7vrQ7< zeb|>TS#2^nbxA(S$Q6U%eA8qnHQJ`z>IGQK?XnVG!|he{BsI$#0l8iOk*~;u8;(4< zLdVJ6wJn6<=L6`K6KJmo&xr)Our~URb`Of-yWf1%4e)jRuQE6t|Nj8~T5~-o7tr1S O0000?0-p$w8Gi-<004Xc;_#b(x-H~fX*-=oXk#OGN}PE zz%Hhg0@pfN{$0v(vOs?t^tV?h)6dt}*Ej0?{QM1i{JC1K76h;{(R@A!V+@3_8$hH0 zSpHdp_x>ItAb-MSI)&M6cDrtyx4N>`7o+V$?#_%!d63c1bT0lCsW<75r#Y*tCXleO zrn=O0>Af}@wyj>1^8wdR#*47l(mYy@dG+kvS|jIVr+h_LDmUe%7zgiVG#!Vpu4Tm# zXfbtNL6#B=SvEVDIKu+~-)yXgeT^M606VqDk^)sPp?~j}XpGLlDN`vV38N7nK4|H*j8PwZ8(%Ceuj_GxopN2|z3JuwUt%vp*%zzQ*&@Zj%KK z=eu8`(n?|^iOAcJ6oCUc&mp#1PvgAc7=}SOy7d6GG4dG;&|&1?3BX7}QnGqDT>d=` zKoXI+oqqu6haE8%fSeG3<)WlbUaiIhka|Fsig!dfe)z@g5P%;R>3DPiHmyg}KKX+e zwgON93P1rU00p1`6o3Ly017|>C;$bZ02F`%Pyh-*0Vn_kpa2wr0#E=(1K^e*M8DCK zG712{kLFem$L%}=g7FkU1mAA<1B?d1Z(_Y~;eTZSR8#=@b#QnI08$=(l%JC^?=2ez zfIBgG^+9$*KMzJsLN6@=!?*Is2T)P==(1ty&VW>rdpCzPuWapg@oiB$o_>S%=io0l0M!xe#;+tVB#iH3ER@0DP8pe263E zK!1!iN&(6{_1u#cI6y-U7rtTf!6zv|&b{-obnXEnKSFx*#+{I$^yB^&tX3;<&Vk;e zuPg5-D;Gs-Rgny921$(z~rEjlc0e=6@FV?f%Lya!X&^wy{`r*~bcSICg=Mhmgo0 zHpa=d;Q7*b?Y#W}NrkR(+;QBylF2eU+6~QJ1G`v^wU=CypXB*Jfw|V-no55Xw2O<2 z*WQPZDuj0&GDixifn?lbT-9~2iv8C#0klq}9_Y3rh?}{Yf4%x~_4^;J;njFi?hd;% P00000NkvXXu0mjfm{rsg delta 294 zcmbQwv5jehWIZzj1B3UUhE+gHG{7gs)i$%pHnSLrY`~;mP?TwW#zKW0PoM&pk|4ie z=BAU%zfVZN2l6=!JR*x381$4un6YB1eHl=2yQhm|NX4zU*H7~uQQ%+=aMWcy{Q+Z1Cb zFs2>2&SHGw5?7{q#m+@rmYjXCYlYYYju&DHwocpy93|Y>e*b4EeQ=P55zR#2}JzgyR m&rqJ(^E|UybkmQT9}HXa4OaZ)jr0L}g2B_(&t;ucLK6UCNN^Sa diff --git a/examples/starting-physics/assets/StartingBlock_Heavy.png b/examples/starting-physics/assets/StartingBlock_Heavy.png index e2a7c7420bfac71591d7caab98301b69f359a354..ba22139737a3fd1b7b320c3f10025d7e23a2e46d 100644 GIT binary patch literal 517 zcmeAS@N?(olHy`uVBq!ia0vp^4j|0I1|(Ny7TyC=oCO|{#S9GG!XV7ZFl&wkP>``W z$lZxy-8q?;Kn_c~qpu?a!^VE@KZ&eBK3|DzL`iUdT1k0gQ7VI5W_oVoyp7Y6fw~$z zT^vIy7~kG@^gHArarC44Jv00L zdUezfmOmThYoFxShAW6V?eRVTm17Uv*?%|QUQFg^U}dnR9@;6Az?-CbK>JdguU+QZ zV)^79j<>Q;9lv?&{hy+pH9dlpXP9wL?lASqdHelq--}H(^Pm6xIlFjcdg8Bk^DU|0 qcbrYkm7niaaDDaF(h9*IudAo8s|zOe^mRZC4|jJbM@KVL(>M1v zEe5J&D+%%oK6IGvUaQ^CTQX@t5zYdS$YKTtJ!KGPtXOJa1{5s!ba4!+xb^nBBVU67 zhfAP@#?{~P5k0dq9v>4b|2%!^9$Ti&1G<}*+DAXWX7Kv-zl?dOo6?TYfA1iq+gM!B z#nD_5(onEbDnZCpfkQySp@D&si3Q9!Fu&m%!&>=y7q}KwTE`#aS@8QSpUHKfwlDMc d`0i!A!O&}z-W9FEwhib822WQ%mvv4FO#uEpRU!ZY diff --git a/examples/starting-physics/assets/StartingGround.png b/examples/starting-physics/assets/StartingGround.png index 2a66b69a02b5edb6a6a4bcad3f4f10f259b5d5c2..d181229556ba7aa613300ce4062e7590851e4d06 100644 GIT binary patch delta 655 zcmbQoJco6HWIY=L1H-D!!h1l9v%n*=n1O*?7=#%aX3dcR3NjW4xjQkeJ16rJ$YDu$ z^mSxl*x1kgCy^D%=PPlIC<)F_D=AMbN@Z}%OwUc6w{coB0|S$|r;B4q1>@Tp(Rob) zBCh5gOi|e;Z{6#yX7(*EekowJ&~M@7j7$Cd>U)&*&5<1{o(Z>QE%L-egQopE$#&+sq1rw3 zw96XSX1&ZT{{;5_la=aKJLh@xm%H=?9buNqiiZ-W$Yz_x&REuI@oi(onU9~pwtWkg z&)Z$kep#cue6II@4dx@c`F|F=w74C1`ucSNUemladI{ZiqJ`-khT6_5S5x-Xru==LJ6zxKQn!i3G2er3tr zzWOzz&gpZ1_e~U2X}y_gxcK32EhR4XSFbFCN`6_juKH5nd-wRmyNb!{#LKwnH0;@S z``Zk~(5-dX7%QFK^7QAvTM)qQeQ@oU@0-#@D%smZVt<`#aCTm%ku`VTn#XTsgGygX z`|Iy3>kdPf^#^S1=*B%TqHW49CT25 z&LYuaAdsjq>7YYP;|VS{rEH0g7!DJLS%)21SQ8Zv0Yy2OC-U;R9B@$ij!UgG^8!nt fmc@G+_v^5ojCy3-y$YCs7&ts#{an^LB{Ts5>o^+4 delta 399 zcmV;A0dW4D1)c+t8Gi!+005o0f$RVP01Qw}R7HWZD4D`dlD$KTw>KgR0jK}~010qN zS#tmYE+YT{E+YYWr9XB600B5jL_t(|+U-|S7J@Jg3+?^yEGoj*CZkioc<|waX3a*) zx`^e+3pc?{kO<5HH$9r*9l;7eFPHfQM^6uX|I+*?;t@wj?|+|h#;5o0rYC69jJ-w^ z>~Q3Kx(JDb&lwmJegq)ix=SJNw1=Ro)>NBUb&GOBvRw?QlhMj#jA2t-57S7B`My1QOF|m0P#pN1``D~NLk{iNQ zo5wc?iF1OpqwItCJ_*D>4G+_ZLmlVU;~q6b5%FWW5caQZd7w6Y)pVg|>rA-Tt)aL! tmZ!#ZxZ!KochX!Z{Qk#s6a0hV0d}(x7s>EvasU7T00>D%PDHLkV1jh>v#$UE diff --git a/examples/starting-physics/starting-physics.json b/examples/starting-physics/starting-physics.json index 04ade7816..c145af93e 100644 --- a/examples/starting-physics/starting-physics.json +++ b/examples/starting-physics/starting-physics.json @@ -1,7 +1,7 @@ { "firstLayout": "", "gdVersion": { - "build": 272, + "build": 274, "major": 5, "minor": 6, "revision": 0 diff --git a/examples/starting-platformer-pixel/starting-platformer-pixel.json b/examples/starting-platformer-pixel/starting-platformer-pixel.json index a97a70202..7ccbf355f 100644 --- a/examples/starting-platformer-pixel/starting-platformer-pixel.json +++ b/examples/starting-platformer-pixel/starting-platformer-pixel.json @@ -1,7 +1,7 @@ { "firstLayout": "", "gdVersion": { - "build": 272, + "build": 274, "major": 5, "minor": 6, "revision": 0 diff --git a/examples/starting-platformer/assets/Background.png b/examples/starting-platformer/assets/Background.png index 83470d7ee7b6b56c474728e849f9b61fc8860c4b..313afed01e19a3496c7e7147c0c31af83fba1bd8 100644 GIT binary patch literal 381 zcmeAS@N?(olHy`uVBq!ia0vp^4Is?H1|$#LC7uRSoCO|{#S9GG!XV7ZFl&wkP>``W z$lZxy-8q?;Kn_c~qpu?a!^VE@KZ&eBK3|DzL`iUdT1k0gQ7VI5W_oVoyp7Y6figcl zT^vIy7~kI6D9Y$4!g|48%RxzDfsv_(<5CuFg$St)+0OzFpLti9Z}fZdcHTYHcU9Xj z>v&zZ+0MoF?&A7m5~n}u?vMSaw0!@)!v%e(+P)W@Iv-m1xCUr40wl!cegAjKcHZ$7 zYf`4Qt@^X==gKRGKmWazdu{a!jpuQnHvgadN&2b&sfnNXU2A@lJ`mkqwgfboFyt=akR{00FX$VgLXD delta 181 zcmey%bc}I=WIZzj1H+dG6Mq6Jo&cW^SD(znXAd7jka%6WEl`}fB*-tAq2WN(#CfVf zE@y#9WHAGSo-znCRxGtI0}2Lux;TbZ+005u}1^@s6i_d2*00006VoOIv0RI60 z0RN!9r;`8x010qNS#tmYE+YT{E+YYWr9XB6000McNliru>IW486)D;&{k8xA02y>e zSad^gZEa<4bO1wgWnpw>WFU8GbZ8()Nlj2!fese{01gjHL_t(|+U=cxXq@+Xz(3D> zC+SX79NUp>$yVSJ$>iiJ))lIUFb5nqkA@bSF^@SUfk8d!SOYF+9(=fJWpSxPJu{Ww|g-LpUR+CJNoe0RD#-Fx?Z zz*x5LUcdLeAD-v?eDC{yUoebKL@WgNh(uz+rWQAX1fn@1BG3aG0qFpq0NQ~LAn5&^ z&m55PexCtuflh)EfITSm(lNuxSW*0eB9GRYA65q#Of&1Dq9i`D~*IS=R#gJ@af2 zj`KC(dEj6T3HK;6z=yyua;ooNPMlAhfPRrKIz9et%)c z1gyd4AsE(!z+VD~e3a=*$&6<}hm+&y&zOL1TFX~8+}1|tj&1;^r>?WOFkkB#@jqOZzpk}x=+so!i|#vw z1TNINd`->Gbad{#`{yS*chc0{TV(oQ32JDOOiy-oAe7JDH zah!rByuFkmm&@Tpl8C;TyjZ>UhgB1>w|{Uz;55*KQOisyyp$Rr9jluX+#4Mn5;$k_ z`GOvSbG^~(%okQ%z^D5M4~lS_hwmaYYL|8qPCwm0c+l4+`04(^gASa+RZjp(-z>5~F8*BcRuCoU^i9H_Rz~wGxd<*~fr=(R~rG+co*> zX-o*G`}XdO)N}^ivp3eRsvpyMHwZNfNmTwInTVHdrFGddU?ds~A~t>^m~ zn{H_|GKhY8qw5JbRDvVXSO}4mCSOAmLgiE>8VhZjfZj6t0lO9rOVo3q`X#uxe{e|P zf9x6m?aypfs3UgP&wha;CZDmv zj`(QFC0O8F7DHo$yuRMIBhK38ci%y5!GW=H`n>1(6`27=Ubom8W#VX2O-OJ5vmJtS z#kyakOt`3YuGWmKo&m-AhRJ7?ksvDzSqdw`qI}b0W|Wz)7gU11ZFE1icp2qJbGau| zpIn$&KLd&!vQQb7Kpf|5g_Phwwon-r;)Qif@SZ)fb`9#ZH6TW%x?yo;+oenKxm7cu z26V{eGb-5)Yfy*QF2TZ(#mJ~;8GdjK_`Jo(s4&m1ItKK{e>d^Tv)hdN7zf_XfEvv0 zH~EZei~|YifmKSd9I)6J)j);S1Z2O3#;6hls|omw#l@%+U%IaeiNs>T;F2!d7|^Iz zBjPSfIGv8W4nR{T+h_6_)oet9ExE|u60G9bvptOJA`;CX14b+&Ms@uok}se=NZP0> z3cK?JR1?ssHc#aVD7NjIQEfW%OYnwwKiIN$8#}so<2a6mwoY*zht95DJicvvIS%&( z+?#rNqV&O}u)T99ZEY)$2X1Z&ky%`@t5t_Iw}c3XTM36-2?m?^eC7tKTI%82^93yF z;nGeZKsHwS`?p;rsd<{%XC`9+JCuwSK zF6HgPd;yEffZ^6Qc6Rp=2-=q?jZPpKq-$3X;kKfSXnVeZ1rus*rE_PuH5*1=AK=ir zql-|uwfJFN(eF?TxUrUqR9?W$;sP@_uA`c>h#LUpayh1_uCutXP_lq?#k^@@exAvz z6J%Z2qTk?TU6-kA6D%&w7yH)x?g=T{q~+z?T)%dOrA)@ILW8ojl;QfdD=aVHF6Hf+ zd;w=lO1;@EHzuz#Klg=QjRs?3evTX0ua-IocsB#ynMau~^(d;!XVX)-?lLVcA#N{O zMyywEFJ;IqF0zza;MVNTjUH|$U%;7-9_`l6&+Q5|K(ldU@@lybcPp<1>x_lhs4|mz z0!rHAVN{tbcLh|Kuy`0%=IXr+sA?I|s4^?gIRK85uz(m<)lW&@6>vH1q$y!C+AvDQ z&1Rf?0;c0}7c2`JRU?s3$KAXx7=YhdT#PF5$(W|=QDMf`d`<|$Z!JDXh51eXAMO`0#|IW4qr#kB zQ$WeeSsMo$6(l3BtSz8dn}20-GAhJ}`QyKbOYjO-hK)+`iwFN=6#;WUw>TMPYV_>nTXE< zzqGg+W#UX~JU&x+23$#*CoFD8nHXN{AJ!Dm4apf;SvGAL8z7w=A3amFfZi(UVT+%! zLEc&KAJ^ZWP2~hIYcVuRWY%kFOB2v*Ml1^&C86+R&!G}CARsDl<87s77)6jl<@lps z^hg0OCE}Bo1&tE2-*9Thos9|TWx?Z?1&wlNzk&tyvfy_uu0}btU%>);9r4@N5gP?C zDJpLj`r<&LFZ_IFGPAw2>lSdxVr;C#-y{>G9~S!Jq8`)Ww#W(W26kg@;_f5n{Wc_E zIv#gbdClT%tU5|l-b^RrnTiPrkV=e>06(;F8xJt-xj{t*^cs?HW4o#vdBxFHIabOW zOA;`dh|em#X>m6I6#k;v(pz*F{2(`HrhdP@v#T4}ZnB=M#!e%{1V+@3mASzTxT@D96d% zoS8bmy|b$m_=;VchTvaQg4 z1#_zM)l?#0_Jt@;CC)Z=@%)&=%h>cxJ&;l5~~2K|5#ML)^+@9v`f+2v$>q1ykkZ04u#>mPg-c-Jo- za>tG?THDs|qvq$n;Iru|-*mbPucgLE|8uiWKsGx9y?ggXMdc(gQ0rGqMA*^2o0c`k zf=ihUHzu#5s(z3!p(=;Vv82jZCHO&ZPEXBj>FEBEBXr_}<^78b^R%@+hU45nho5y_ zrY5hV{%AgiU6sS17Px&zU1z}ivf;{n;g5hwEnmH+re=2T>bWbSYL3aP6D%*^u66My zRCzO%7=6EH#@4kIotraL=`9`I!?m6bbGayN!>PpR`I;Km z)M~VPqk{)T_yG{B)pHEDwxSA|#f4fHZ%mcfO1Wvgp#=npL}Q_#=n>!_fG&(;m<9gM zU6d2)bi8ICIwXFGq_@)gE#P%*QNyFi0N+LB?P^ad*R6ne*b|L)spxl1#MQ-97)IrI zss_5-x)g8~5&s<6YNeK8Re9S_;`v&@{L-wVM+Ck_jZfWFfF#N=$O%7`+PW0*ei;W3 zBD?|oQ5~0O7x*R02{$8WYPq>+I05g_+aK#d^&2956F5*OA8Q2o87lAjIomaafDe@D z2%?98KLMWeQD$_3UjZMGlb37exR7 delta 928 zcmV;R17G~79jgbB8Gi!+006uZmmdHC02@$DR7Ff_aWyqHHa0djH8nOiHZ3hJHa0dm zI5|R^?&H|1(F!EEFDZlRnwi_ z{vfi zj=HzjIAJ?^*aIR1yr3o5f0`Hd%5^<6F6Up zP~Fjl;*KVq#|bI9VKHMl8{r`)iPZ%{-xG$jf4=?q~^F0)IA24NSmi2{R2agHyOAVdeoQ zxPbQ(79!wkIWZ#NI!%xsUi&yP0Snv1t6i5;0fp`1zdpu#*d3k2|9q@mKxK#E_j(Kt z!LJ|`GN7)T^niore1Z;j$NmwypId1iKp=wpMU(OX1$5-Pn-+2DQ3 z3U8HfoorJ0VFQ$enSS6!7WRpkSLewMoFS+@5$ws+?STP?b}+=r0-O&JIbb}yTqpDq z_kD-lXD%rF#snxfL8d2)U`M4yN4i*I^UWcQjl?nfK%C*3a$s;&f%6R|JUAMx!11?+{;EmT?0T5AdBGkQj7xl&{aQ(}c)V~^X*e%3^Jziw;PzI z-Q)Pxrd*^ diff --git a/examples/starting-platformer/assets/Flat dark joystick thumb.png b/examples/starting-platformer/assets/Flat dark joystick thumb.png index acd005cc067032996264e510b58c8dbc0c102b62..8acf179f8660be36ccea1198105da35c07154fd7 100644 GIT binary patch literal 2939 zcmV->3xxEEP)IW485iPkW{Hp)}3kXR> zK~#9!?Oj=H8`l**_stBok)rm+alOTHY)jTcOO_Pdau+1&5)^3yq%HEtw|uhZ1*opyTSlNpA;d31v<-mnz&SAh32>in$=8J7LtSkrZm$^ucGWa^ z6!RueX69Q&R0kk4#epydaJxA@{SzY5+uL>0ye|c)so$qD^Rt?!y#%6%(#ihjpa33} zwtSU{|Jv1dLf$t5RM$Q7jHcj&qo=AHvH zcS_s(QP1g?WA~H*Teejd`1Ae0AQI2xJ^(YnIXQgmg^QO4CevPk+J^mmiNyN=@{&o4 zVR(>{nF#=xot=SYCOvlsnbnh>CyyRZO986tn{)y3TcPRC1JIJzWrHBEy*uVBgT{=5QcnwD$<2%zoR zxp}=+b&ZuN6`;C)U$gGXI0B%=iDW1=4MHq^%0dWen(kC?F4y&pqt$i$8dD-bZNvU= zX`1#vfc&(oSinvmE>F|7LxINTCma78=>)(5BZ0rEo-~mj}0bVfbBa1dD(v70D$6j@OmKx zgrIsyW}V4A}ur1`-h5}JruV9n#c1v5q;6+E@4)*S1PWL z%YrIv8+DJz(+Q$Vm%0RkxzDn!n%<5UJ8BOQLNtJ=!euPs-C!qx#;6vwqavW`y8af@ zZ_dtrA%SifuWhdgXiEy9XJl*u(H57hq~(G@me*Unv;ew(5P-*}Drqp7Xs{p(%Pf0~&CK7p|zqdWKpaih*^GR zUH~Dque+Q$3qjg9?+}0*TuL170z&KoAZS;hvEJ}xoN_5~9;RsqgsvOz&_k*&i**6BnP*W7-?<3WC5F?_yU*p`Lh_$VeO zLg4h8mF9dkLj-?b0rCn8p=lZ-;Yo}R55cmmc)nXgwGGX;i74unL$5CvW#tcmKDRi+ zG)-I`{20t?&62-i^Hz8~3rFx-Dcrhw9pTA|SQa!PD8_0=B0}jRvUAq%?SpLZ((6-%5XkYyTE9X@%+XK7 z6a5C6t{cf-8k(jB48t&tRdF{z6cG)v7C>8;1){Y#jz>hWZEIEBeJTKR)L)5&C!@~4 zkAx?&RuPFrmfWV4!sJ9KRzI=?12fTfky2vl=JiEIFwI#E58Yg|0^GiJ1Lh)8u9U*i z&1*5OnHfPHzz|}rb_`EWU}k0-Ma8Aa@%kX8L~v{rq2Sn>^#41{&8wF`MsBVjdHIFV z4Fi*t6Br*Ij`!IWA)T3TQykCnu`LU?Zd{wY+H>#S+$lmRIF3+oeA(PE^N;}Zjb)}E zm-R$(!+b>$QJYI#%1Wt0!N4__iIm}tur2eP%R_2lTh=84fI#B`cZ@j&`Maa_u$Y^c zqTD>-FqBfK=7%Dblpnd2I4;0D^8%12K7f0x%TfCwK_}(~7#|z!0bshkI48h#DBL?Q zz}1WWQ%b4BE-%hNO8LRS`L2n10RTV{{miu?XQE#S@$-4!Kl@`U93BKPOm0sw$is)d=)x_l)%__?2zYWe)< zcL{K|`;BCp8ZFJ5c^$f{XzIlF}*ihfT{o(A{=yRkKM}UB8{_Xzd9F$F@0f z4?#+aU~qiN?ZNReNCyO9=8nmU;6rCRT6{tZx4-!*1W$G`LoZ*uIxw90 z!Xk;N3_Q}H>)M+_h^l*XOh>6MY1=>S?YPI@DSy`x#_pyc=(_O&-6KzCXXZ zv-R)EFRdNIuDT|TB|m3)JU=C(O-R4xmdwl_o9666A+$gBo<8A7J?l6L+be1_gl??& zWMuqhu42kcCFuZWm{li0Pfmp=C(ieE&N_JlDUHI)+NLt0(OymHuaKY}b7e6(K)Ph+ z3(~e*zRiP*%l>rgtnHC$4;H4S$509AMTK!o)q_;4Vv8 zq-%bJcA5O|JulJAeuQX3P>IsnfaZ6Kar2%-@G*)QG=HZE&goRnsb>)#yPBG1sB!Hh z43d+OIs3N_GYkgUzhtn9^S>bspW_gq{VNwViuO^0PYl=T0VPPUzF*M(>6^wL0BHZ@ zvWDhTyoJGYF@JnE6Uejc!3TG#~e;_b+x__nEUko!5E+){gO+8MW_ zw!W>|D~;a);deGMLT(Z&UC$fvvaWY6#qkPCAl3O6NYLvOv|0Q7ArXJfHO(NbN$JSE zv_5Q9<57-dJ9nTwHg>n+?}Hn0XP~q-FhZf#^$lz_!DVVS@Tp(Rob) zBCh5gOi|e;Z{6#yX7(*EekowJ&~M@7j7$Cd>U)&*&5<1{o(Z>QE%L-egQopE$#&+sq1rw3 zw96XSX1&ZT{{;5_la=aKJLh@xm%H=?9buNqiiZ-W$Yz_x&REuI@oi(onU9~pwtWkg z&)Z$kep#cue6II@4dx@c`F|F=w74C1`ucSNUemladI{ZiqJ`-khT6_5S5x-Xru==LJ6zxKQn!i3G2er3tr zzWOzz&gpZ1_e~U2X}y_gxcK32EhR4XSFbFCN`6_juKH5nd-wRmyNb!{#LKwnH0;@S z``Zk~(5-dX7%QFK^7QAvTM)qQeQ@oU@0-#@D%smZVt<`#aCTm%ku`VTn#XTsgGygX z`|Iy3>kdPf^#^S1=*B%TqHW49CT25 z&LYuaAdsjq>7YYP;|VS{rEH0g7!DJLS%)21SQ8Zv0Yy2OC-U;R9B@$ij!UgG^8!nt fmc@G+_v^5ojCy3-y$YCs7&ts#{an^LB{Ts5>o^+4 delta 399 zcmV;A0dW4D1)c+t8Gi!+005o0f$RVP01Qw}R7HWZD4D`dlD$KTw>KgR0jK}~010qN zS#tmYE+YT{E+YYWr9XB600B5jL_t(|+U-|S7J@Jg3+?^yEGoj*CZkioc<|waX3a*) zx`^e+3pc?{kO<5HH$9r*9l;7eFPHfQM^6uX|I+*?;t@wj?|+|h#;5o0rYC69jJ-w^ z>~Q3Kx(JDb&lwmJegq)ix=SJNw1=Ro)>NBUb&GOBvRw?QlhMj#jA2t-57S7B`My1QOF|m0P#pN1``D~NLk{iNQ zo5wc?iF1OpqwItCJ_*D>4G+_ZLmlVU;~q6b5%FWW5caQZd7w6Y)pVg|>rA-Tt)aL! tmZ!#ZxZ!KochX!Z{Qk#s6a0hV0d}(x7s>EvasU7T00>D%PDHLkV1jh>v#$UE diff --git a/examples/starting-platformer/assets/StartingPlayer.png b/examples/starting-platformer/assets/StartingPlayer.png index 472f7be2998459c3b5686c8c200d2d72c2046a97..89dd22451ba35db6f10af1b0c18e546774701362 100644 GIT binary patch literal 2978 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 XaJ7l|q5$ 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 FV1j1w)3N{n delta 627 zcmV-(0*w8$9DxOp8Gi!+006nq0-pc?05(ueR7Ff_aWyqHHa0dmI5;&mH8L_XHa0dg zGBP$cHa0dk|NsAAUtjX_^4{Lw#l^)&Mn->se~*ukx&LcZ0000AbW%=J0B>IZjxp~R zy4HO*rZ@lq010qNS#tmY3labT3lag+-G2N400G-cL_t(|+JD8F>ypAC4227X5~x;v z|ChDy&bnGlZe(`P@6Ii$Vi%H32h?z{` zA&({os!ck)jd&rN1@*kv&X;~w(7+>zIGK0^u?M`ITYobVd%(4ET6Mt2>z{${OJ<6^ zsnagq<8`6Sd|rA-EH^URbgQvHQ^O|E%x{@$LOs$9eVLZ5Rh; z*XQ!VgKQZc-=f2o!?Ed*2lmdfbz>*?te|XMb>FPA<&m<}EJWI#E9%{YoXZKS(#-Mi zEz^go>*bgJ1}U?hv%<;dkt!VPW>+x%4v0{XX>l^EM0ovUQ4t~&+a0mURTHt&FNy#F N002ovPDHLkV1nr&BUJzZ diff --git a/examples/starting-platformer/starting-platformer.json b/examples/starting-platformer/starting-platformer.json index a4ebf3c47..715095d33 100644 --- a/examples/starting-platformer/starting-platformer.json +++ b/examples/starting-platformer/starting-platformer.json @@ -1,7 +1,7 @@ { "firstLayout": "", "gdVersion": { - "build": 272, + "build": 274, "major": 5, "minor": 6, "revision": 0 @@ -206,7 +206,14 @@ "gameEditorMode": "instances-editor" }, "objectsGroups": [], - "variables": [], + "variables": [ + { + "name": "CoinPitch", + "persistentUuid": "a9ab811d-91cf-4b76-95a3-81d9cd86156b", + "type": "number", + "value": 1 + } + ], "instances": [ { "angle": 0, @@ -262,13 +269,13 @@ "angle": 0, "customSize": true, "depth": 0, - "height": 120, + "height": 104, "keepRatio": true, "layer": "MobileControls", "locked": true, "name": "JumpButton", "persistentUuid": "b1d437ce-f3b4-4799-875e-bb51f731cef0", - "width": 120, + "width": 104, "x": 1104, "y": 576, "zOrder": 3, @@ -390,6 +397,70 @@ "numberProperties": [], "stringProperties": [], "initialVariables": [] + }, + { + "angle": 0, + "customSize": false, + "height": 0, + "keepRatio": true, + "layer": "", + "name": "Coins", + "persistentUuid": "c89b455a-90ee-4bdd-b31f-e28e45f6ebe9", + "width": 0, + "x": 296, + "y": 481, + "zOrder": 6, + "numberProperties": [], + "stringProperties": [], + "initialVariables": [] + }, + { + "angle": 0, + "customSize": false, + "height": 0, + "keepRatio": true, + "layer": "", + "name": "Coins", + "persistentUuid": "06a18fa6-e103-4ac4-8498-286721071ebf", + "width": 0, + "x": 104, + "y": 481, + "zOrder": 6, + "numberProperties": [], + "stringProperties": [], + "initialVariables": [] + }, + { + "angle": 0, + "customSize": false, + "height": 0, + "keepRatio": true, + "layer": "", + "name": "Coins", + "persistentUuid": "80bc1e19-cbb1-44c9-9787-6ff9d64b0c9b", + "width": 0, + "x": 168, + "y": 481, + "zOrder": 6, + "numberProperties": [], + "stringProperties": [], + "initialVariables": [] + }, + { + "angle": 0, + "customSize": false, + "height": 0, + "keepRatio": true, + "layer": "", + "name": "Coins", + "persistentUuid": "541cc5a3-eb1b-4ccd-97ec-4a2c0c2278fc", + "width": 0, + "x": 232, + "y": 481, + "zOrder": 6, + "numberProperties": [], + "stringProperties": [], + "initialVariables": [] } ], "objects": [ @@ -549,6 +620,7 @@ "adaptCollisionMaskAutomatically": true, "assetStoreId": "", "name": "Coins", + "persistentUuid": "039c827f-a216-4ae1-966c-af24e56a91ca", "type": "Sprite", "updateIfNotVisible": false, "variables": [], @@ -609,6 +681,7 @@ "adaptCollisionMaskAutomatically": true, "assetStoreId": "9c727020616afdd6ba786b8af206a90481f07db0ca175ed6a4cc5b7e01c66d06", "name": "JumpButton", + "persistentUuid": "1f6fcd57-2e3d-41ee-b7fb-f884711b4cd3", "type": "Sprite", "updateIfNotVisible": false, "variables": [], @@ -630,13 +703,13 @@ "Scale": "Scale", "ButtonFSM": "ButtonFSM", "Tween": "Tween", - "IdleScale": 1.5, - "FocusedScale": 1.45, - "FadeInDuration": 0.05, - "FadeOutDuration": 0.1, - "PressedScale": 1.45, - "FadeInEasing": "easeInOutQuad", - "FadeOutEasing": "easeInOutQuad", + "IdleScale": 1.3, + "FocusedScale": 1.25, + "FadeInDuration": 0.02, + "FadeOutDuration": 0.05, + "PressedScale": 1.2, + "FadeInEasing": "easeOutQuad", + "FadeOutEasing": "easeInQuad", "PreviousState": "Idle" }, { @@ -835,6 +908,43 @@ ] }, "events": [ + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [ + { + "type": { + "value": "CompareTimer" + }, + "parameters": [ + "", + "\"Coin_PitchTimer\"", + ">", + "1" + ] + } + ], + "actions": [ + { + "type": { + "value": "SetNumberVariable" + }, + "parameters": [ + "CoinPitch", + "=", + "RandomFloatInRange(0.8,1)" + ] + }, + { + "type": { + "value": "RemoveTimer" + }, + "parameters": [ + "", + "\"Coin_PitchTimer\"" + ] + } + ] + }, { "type": "BuiltinCommonInstructions::Standard", "conditions": [ @@ -870,7 +980,26 @@ "assets/CoinPickUp.wav", "", "80", - "RandomFloatInRange(0.75,1)" + "CoinPitch" + ] + }, + { + "type": { + "value": "ResetTimer" + }, + "parameters": [ + "", + "\"Coin_PitchTimer\"" + ] + }, + { + "type": { + "value": "SetNumberVariable" + }, + "parameters": [ + "CoinPitch", + "+", + "RandomWithStep(0.050,0.1,0.025)" ] } ] diff --git a/examples/starting-point-and-click/assets/StartingGroundTilemap.png b/examples/starting-point-and-click/assets/StartingGroundTilemap.png index 74f0bd521f7892ac6bdeb35bb2a1dc17a290e078..d25eafc90673351ff18572546c38f6ac7e1b8481 100644 GIT binary patch literal 1847 zcmcgsX;c%|623%e5Tt-WL|Vg&0tyBoK_eB2su5 z)<7VlG(kal&s5nY1Y@9;NGt+MvtZe@tYwjq1d@i+zy0lU_5uE{mq1l$}fGUQi zwg|n54i1FQKrKGCH9dQrcUODy8Twd2#FgLYrK#y*(KdZcW8S*7nTut`U$PoP;88t^ z(3&P&f?iGRwGK$U-i(0*hnjytA9FkM!slH#Os3pCq>oE@3Xvhy!#X0(YP<26=)hgO zKA%fm`CbW$Kw#rSQq*maLPyL6(%60Bu{2BO5cj?z*1aH=9rOcx7OwN{Me^ok1B&gX}IZR1U@(T3@<-s zdYJ@ab&W)a5m?!lE4{B%vggY$UKN=>68~iJ;0a~^?L59PyMYSW zkT0Z3QTca1W2#V;F=|mgVbP@XhnR-A9U^9Ffxy8%7;tv;`epYR4yfNQ4HQk0UkPon zdx|nHlTn9SOd)xPIYe_i)$p&u@Ep1?ExO|ip>>jbqdi(B^wJTIG!HiIB^LT(*&S3r zqk$^q8`TP^i2j3-1CmevHKRHoEnisQD6}c>*Ms@!2>+Y#%fTnDEzycBt4|kGcX`eV z9dw)~0*2Hf1WwN(Axx%UJ+S>^sw(7~;3UA@>*jnoT6WGhNAZ}YE;~!-eHwG(_yh@^KDg>4Oh+`|!jXG5tFuj$S<~v)JxmcaeuIt}tLGn-M+JqJ;%n^7 zaErCWtBR#-(J*3WsN~Plm4VqAyB3NJCAP{s*;ukMfoXXcrZ3cUlneN`cS$F?EA()* zy`zWddWvpN;H|Aj)sX*VAGCd?pG}tEj=D2QSe0&$VAL?tt?Z<8cFAdfpL=OCXBnz? z61Njf=bc*&T<#k~wXO%9)u1VMI`JcANauwW&+Z@9aP6l3UDOB9QXo8R0j;rHxv`$; zH5=JShE+GRcV8z_3GM~HHiwkY*;orR+_I!WDB_rnl&Ly-l*B+kBHo9qbT00f708vDJ+SR@M zhK@PAlAwfY6({-W)~_0T+h}9B`18xb2vMj>+Ey>eI6iWbL1M2w*g!uh*SU8IiY{`GBi ze>m|S%9RqsZ+~g&mpU%Bxn1!~DUr}xk(+SEi>zXnloA)Vt@Mc}3dYFJoG%Zp$|4!7 zn!Yn+;%vkq(mmJyW!sgBwf?CiW0~w zJGS|0M7+1CaI?-c1FqDfyc%E2E@R+?)5YrO16z;-00ewAa2Nm`pbJdE#{~cOx~ONM z3cIbG7S6984Nrz(+SM;So;WdFEuxOU3-IU;mo!z6nz6R^KNHrL7W{`*QQvdF<*O7| z=ct{9JqYf8G*_z!qlVTolM)mO;>0i4j2DVU&9k;zW!Oc`bhy{u^(eHszM-b{p~2~^ yaw57DyBl`+)embCNXDGG=Pb>fn0c#yPy^P}>Mk5&n-yzW1JHpX$C{7CQ~w6AS|^$S literal 649 zcmeAS@N?(olHy`uVBq!ia0vp^2SAvE8Azrw%`pX1LIFM@u0ENCFP=Vq{^ZHa=g*%# zeE9Oki|r@bT7a@FB|(0{49Dy26;{rd0`fTvJR*x381$4un6YB1eHjA-6R)R>V@SoV zw|7tXH5&-H1pfN^f4|MPgN$nwv$b~Z?P$62Wn<%nPbW4n&Aor>UR~Yix7B<9+I*dT zI4z4=?)1i!y*rNn4R}9m<>^%UrR+C0o%1wsZ3urVGDT?t?{ASEY74g3a`*^e_$}A# z-pTY!CUnd5i5dy-@BAq+nK=9AFV(-_W-lnJ+2(z+J$=HGyr!F5WBHw4 zszt1hb=&@(NB4lt?+Y`Umw!vmdK>@QQtQrtCXT{}C$9xu|0Z>O`;`--I``Xh9)r)b zFMig2{`TRXGdyeTwGSP=#{RA_^~KWZWwquXgM8xO%KSU!YgjhxZ~9bkrazbWXMD@r zTz`~PVxe!0kk3x5y5utq!NL>f{|Q=puB7Pwo$p`#pSe~ZtA1OW(f?PcwQg;jt?N|1 zNn82?KJA~hgEdV;QjM!(#}u!kuSc#hb}NKVzu&_6T{D0sK*TjrkN@}^3;sI`XvQwIW&A6-ns>!N*JZK52%;UZV0>h~ zZ`QwQTR+WaJDq=9XMf?g)%mga>|4$I=l`#F$o>7#YTNtof0!d5cSdpUTeJ=25>Hn@ Jmvv4FO#oRk3(5ch diff --git a/examples/starting-point-and-click/assets/StartingPlayer.png b/examples/starting-point-and-click/assets/StartingPlayer.png index 2bf62a0adf55e27d9c4e4e83e68d06e7d4633d37..0ba05892c27e692f74ecc6720078c5f280e1dad7 100644 GIT binary patch literal 3394 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 delta 736 zcmV<60w4Xt8tny;8Gi!+005o0f$RVP07y_wR7JCF1>A!Jzj6ezY6kI+0iIw7%XtLx zj{&D;2Gf26nqCM0z&Y`c0jy{SpkfE9X9uNZ2mk70^OzUQf)V`HQ}V4Xx^)iMnkS}j zm+Sxl019+cPE+bz*Z;oiIqJj*XNjkQ00009a7bBm001r{0Dk~3BLR4&KXw2B0yRlQ zK~#9!&6i1%gdh+_F|f;lu|V+tH*G;tX0a-wqrV$B`FSKmCScn18e7%->P##!;Q($T zPMmQgYBihh6lD^h8ixL53|=FIrvWqKo`Lc3#XSd%HwQ57@uXldM+`Ib4TgRKF92f# zRw0;Y9XNErO@E-3AVk2uGiWEEVHN;*N)Q?h&Y+zjNP zu$5KzEPs4)m@ENnKh7e5AGBU5izwZRP~89&uQYgjsp9KWRh7Cx3X_nWxNCh;)ykAl z!^*F^CT?nDrnD$&>nqh5H`GQ`T%+N&E3QQ1mg>As!>iA$WLJNXKomL@w(W>@Ri?v$ zENrnfZt76>4rlwdEe>sHaoW2ym@^=M*(*2niCO%g_sR_f^;MXo9E|l|`EC_Hcr)?n z(ba*6If$Qq)A)=adb-{|ePaTeK7E77_Z>kn{_*(U)QtfAq+co^hf968#ZSNMt+Mrr S%RRsV0000``W z$lZxy-8q?;Kn_c~qpu?a!^VE@KZ&eBK3|DzL`iUdT1k0gQ7VI5W_oVoyp7Y685o$J zdAc};R4~51?wfTbL8jqjy?(?#KKYDKO|Zt5ykCl!o#sE^o?(1aCI4aVC$+PWdUnzvzv0P z^_03LrZ1N0U;W~@U7pkJtrxfDF4=Esw)8~zid9*Qljo#MRA1Cv^m^CnIoFn4es=ol zC*R8^`~U5{chW8Pdim!)HB2?`Wc@Sj~6neM(*<|&y!sq#&S2SH0o_UkMzwHqn*>={gcnkT={5K z4NJ`I|1~UguB{C_{bgTx-}g(V#}#JBe6apq8) zeeU_^`^WVgSBHML`#O-mY1OJ* zFC?Bz9*f)3{bJWUqvcEgfBB{V^3Hj7cJ_FG`5)XReG68o?QuB{%nJ;ju6{1-oD!M< DdI3mz literal 150 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 WZ6>v-)WdIU6X<9fYE-M)h5sLL=sRKn delta 1438 zcmV;P1!4NOKDi5!89f64003nIoHYOd02@$DR7Fsjs85%tPnV}RhnbneO+}HRJ&T=h zsEv-fX9JH7_y7O^0(4SNQv-`9)WlPfAs2rIHc3Q5RCwC$o!NHlAPhw@Sc3om;wDZU zPYlAy#7f0um&#!WJg`IWHD+$ZV8ygyF#9HG^lt{)!`~r@9RG?y==eVbV#hxdSiw#B z1iYreTG>-D$OahX16bir0Te(1qyqpzsRmw+R>oOHDKqFEEmH|o!PGEUMypynV~T%7 zgZciJzNYfx@N=Efs_G&?0L~F2Q>nD7V`mJJXdde*+1*Szt;h6K%|WXwOn$Jh&iEqD zYSG0zgWV7y+S#&LAx^OSWwuDO{>4}s9DY))irp9BK*bbklHJt=K;+Yg{P?*dO=9vA zQ*3wZ1L9M>UoY`h#S&?sJB@8-q8Mbk&ZOyUbbvS71R4&B2xDQ3ZMWApa2S>00O)alFE?W8C&Gr3t@jR zgzANU$O|Ehq(Pb&g8crmHu8NJqDdNH^g?Wr87~z1S*$xwFLWaEIxmDICT@Q(gfvM5 zj9#cQGVX=Y6(r534Du$I=7r8iUgd>=@y`06ypW|yn&nv!&0eTA(zF;KrYfsmNC6Z; z0Te(16hN{Ug0k>IBvb*Cj1MAXHea8zS>2=p}dLfp`i@Xq$zWctV7Xq3%=~6hHwK zKmin>zZXI_NrO}`#2Crtg@EXV9G@2gk{1G+q(Me6R2TU_ywGzk(j-mpkJ2_(e35;< z5DM}_*x3uE^b1YW*fU;;C$h~8L4PjXBGn5epQwM^s$O2`Ge=ZO zW1GEDV`SV5Sx)1_S*2b`0Te(16hHwK;HSM%v5Zz#?}eaM(jd+FxXR9oRYfo4NXCcN z3spt_lox`Wq_M@SX|^-QC+3A1BDuT}^!GwQ@W{>tn)&!U1gkJ2oheX zE%IV7gwH3^&Abry_d84Z!>OXy$g^I^lB?LfP&`ui0}7x33UEsR z??VXVqlMtTmj>YdxNQ##K>$9Ck#<`ILmK{sAi@jzG1GiUFN9NBwHFFwwuSCq$W?hE zKNg$6u@?fu_~Qg{)(gRDF9a(u^x0*Oq-I;akfj)(mlp!E7Xp9s_#*`SkoYOQ{lW)k zFNA>#F!uFANc2LUJpR$sypTKTh47pg8Y`M7dLiuSg+dKLn2k^RHUTRyQwGL^hgX4lWYhmx(JInVcJv2l9g1Ga75>^R zvC%*2g?#yv-6DkSe;7vdLblNhRc$H#ZrfAsh1|tnDA0fIn)5V-k{8+@J1bOicp*={ z>%0*BQ_>)oy^y0J;O^#yCidtOM6h)4ixaWD5 z;Q%1)xXo5zA}5}F*fZSs(3UaBDR>>)^7E0iaOWduj|niMkNrKzAD;plZJGeV1n=>S sCHRbAD8Xv{5)rn;pAZBw49oEU06q3u7&vGK6951J07*qoM6N<$f=+gzrT_o{ diff --git a/examples/starting-point-and-click/starting-point-and-click.json b/examples/starting-point-and-click/starting-point-and-click.json index 8ce06067b..cfee8d31a 100644 --- a/examples/starting-point-and-click/starting-point-and-click.json +++ b/examples/starting-point-and-click/starting-point-and-click.json @@ -1,7 +1,7 @@ { "firstLayout": "", "gdVersion": { - "build": 272, + "build": 274, "major": 5, "minor": 6, "revision": 0 @@ -154,7 +154,7 @@ "gridColor": 10401023, "gridAlpha": 0.4, "snap": false, - "zoomFactor": 0.5843784410740942, + "zoomFactor": 0.6672773998329988, "windowMask": false, "selectedLayer": "", "gameEditorMode": "instances-editor" @@ -220,13 +220,14 @@ }, { "angle": 0, - "customSize": false, - "height": 0, + "customSize": true, + "depth": 0, + "height": 64, "keepRatio": true, "layer": "", "name": "Obstacle_Impassable", "persistentUuid": "0aca157e-2222-4ef3-97f4-a64878b96278", - "width": 0, + "width": 64, "x": 704, "y": 64, "zOrder": 3, @@ -236,8 +237,7 @@ }, { "angle": 0, - "customSize": true, - "depth": 1, + "customSize": false, "height": 128, "keepRatio": true, "layer": "", @@ -253,8 +253,7 @@ }, { "angle": 0, - "customSize": true, - "depth": 1, + "customSize": false, "height": 128, "keepRatio": true, "layer": "", @@ -273,6 +272,7 @@ { "assetStoreId": "", "name": "Ground_Tilemap", + "persistentUuid": "c5ec5598-12d5-4e26-ad26-24e733e42985", "type": "TileMap::SimpleTileMap", "variables": [], "effects": [], @@ -368,6 +368,7 @@ "height": 192, "leftMargin": 64, "name": "Water_Passable", + "persistentUuid": "31ae478e-313d-4f20-a66e-e610e01d109a", "rightMargin": 64, "texture": "assets\\StartingWater.png", "tiled": true, @@ -386,9 +387,10 @@ ] }, { - "adaptCollisionMaskAutomatically": true, + "adaptCollisionMaskAutomatically": false, "assetStoreId": "", "name": "Obstacle_Impassable", + "persistentUuid": "b45ceb59-4403-4657-8418-185d98b8759c", "type": "Sprite", "updateIfNotVisible": false, "variables": [], @@ -411,7 +413,7 @@ "timeBetweenFrames": 0.08, "sprites": [ { - "hasCustomCollisionMask": true, + "hasCustomCollisionMask": false, "image": "assets\\StartingWall.png", "points": [], "originPoint": { @@ -425,26 +427,7 @@ "x": 0, "y": 0 }, - "customCollisionMask": [ - [ - { - "x": 0, - "y": 0 - }, - { - "x": 64, - "y": 0 - }, - { - "x": 64, - "y": 64 - }, - { - "x": 0, - "y": 64 - } - ] - ] + "customCollisionMask": [] } ] } diff --git a/examples/starting-quiz/assets/Background.png b/examples/starting-quiz/assets/Background.png index 10a4d95ac58d625c183f9990dfaabb6a99d7a368..313afed01e19a3496c7e7147c0c31af83fba1bd8 100644 GIT binary patch literal 381 zcmeAS@N?(olHy`uVBq!ia0vp^4Is?H1|$#LC7uRSoCO|{#S9GG!XV7ZFl&wkP>``W z$lZxy-8q?;Kn_c~qpu?a!^VE@KZ&eBK3|DzL`iUdT1k0gQ7VI5W_oVoyp7Y6figcl zT^vIy7~kI6D9Y$4!g|48%RxzDfsv_(<5CuFg$St)+0OzFpLti9Z}fZdcHTYHcU9Xj z>v&zZ+0MoF?&A7m5~n}u?vMSaw0!@)!v%e(+P)W@Iv-m1xCUr40wl!cegAjKcHZ$7 zYf`4Qt@^X==gKRGKmWazdu{a!jpuQnHvgadN&2b&sfnNXU2A@lJ`mkqwgfboFyt=akR{00FX$VgLXD literal 277 zcmeAS@N?(olHy`uVBq!ia0vp^4Is?H1|$#LC7uRSoCO|{#S9F3${@^GvDCf{D0tk{ z#WAGf*4sNfc^eD_SRAImyOeR1GnZi+$BKr_ndvH%^P1+T)7q>*8F_g}a_-yc4o&-r|Q{G8AEWO;d@RFsY=0RW)l>f-Dp z)2e@=s37AUex#{P_TyYG#sPq``o91HGDTY!f^j}5IKbfPev%ovNJn=^0C<+Kyb~rb z^9SM)f$_fBh{S4HJrWaE@vI~q0OXmj&W?Tw;IiNx);B0(g6AiNsb-6=W%w>ANTIQOi4J`kWjl#f`<& zxV3w%ONnP)3#}_K9CUu7Qa#Kv7|)31{_<3(9({vkfZ1Je%4LW67(3`q)u^r-b~zcX z!e0hOAnAux9#?iIoXn5d1-u`4}|&a){!P&Ut6O-Fj%=@XRD59On!$KLlI{6O)a7BmtkvvnKr3K zbPpg8&JW$RJ@!Ji874s#GhgO(bF@A{8gfZ`#+3a$qojo(2!7pm?fRMq`;1ZsGNgUSBBda%R&dXIlj0shQf(ByS=WKQ zQM8?T*>CK=94~26554gYWH3y026Av%Yqq$b(Iz)9l%UbU=2cqqoA+$%5dK|#eXU^o zHH|1|ft!3;!*_@LH_0c_#t!eL@YI464lW0e*C?v_$SE5AkbVe+X9GVX{*V82Cw?^G z>F5)%{XR7|4o&kezEKIbFG%Q}BQ4RXJo3df1qB6_h2DVy+|+RSm?vQ8BZYD5mtsa=Z4S< zNhY+mMClY~d9oHtAe9r=Hl}|MH*})hg22k!ra!hZ-&z#Y&(BrZld{zGc&yDu&Nq4_ z^iF9sB4J~uPy|hxm0gTi$go+P{S|H!hUfO!)7^8M<%}I(zoI;g9Wv~jia}dY5w~)L86#0cov&-`xs{wHGk56}U$VIu nQ~1a;%9N?jB0T~Dx9xqkALDSP(B~A{y#uZY4`&8E^tXQimD|0$ literal 6328 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 x+VlMVl>qr)eza@;{7jZ}^1s$%`qD$R`yp&Kp~ z^-1F#0b+&^?~!djl}Lah%Xi-SRd8Jo{?;3o-h)_@@SoEI=)F?aM7l^>yqk02c9S24QiaO4jwr%c}IEn=$@Iu|j z{A?dLBatx*q%rq|JS?4a|Lt#kE6DC0Rls21az!T6sTB=BB|cf0=hn7W5Tj2YYh_UTYzD|O1qCGKoBgnDQ^!7 zlxOgcU=(L3SI*wW5@3SY9PjRFnyg5Rw>yp@`_0$geX&9y2t^gk=3+ldi57KzAFG}Z zt#+FP$a-;l>dnP*u|mK!5AdGgZ(`akjHzkN;Zof2D7M=ex~}UY`H(K9HEdOE2BrL~ z=3J{pA`;v$; r5yj*z64LN`uAO!n*j9V(G7kR$?WdIAZVN{t00000NkvXXu0mjfzF)6> literal 0 HcmV?d00001 diff --git a/examples/starting-shootemup/assets/Flat dark joystick border.png b/examples/starting-shootemup/assets/Flat dark joystick border.png index 5ddd717e42b566f78f6441a2a92756a1e612dc74..b4e83e112eb9375cefbb86a6d044a972f8bf4dcf 100644 GIT binary patch literal 3751 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 delta 1615 zcmV-V2C(_39pDU*B!3BTNLh0L01FZT01FZU(%pXi000IONkl$95AT zsS59mS44;|gnx(N+J#8cGO*RzEFP~zospvV09F@@5#H?{@q=XL22*Ds#!`x*5VwTo z4s|@d9%0$U>nUU)7Q7)|FM+)wU6p^Om0hR{xm+$Q0v6(ikQ?aY<#~Q6%d$d$CM1W~ zyWPhizDPv)k<3iubUOVV^8QI7_f&ihfrsPx+j*f+tbbe*UkoCwg&3a*Lr*ruVzFoy zUqm9@#3sJr>w85}TrEIx+~)>gFGPHR2>TfQ+$co2A>jZL6U2uIsq=v;-uI97Tf#!c zjkg1?`t4wW(l~eGoe1k|FBqpvXt>pz0R}xt%4c&^K9fwX4@^h5 zhA#J6GvxxN40aI`y1hS ze!&%-b_4%zQ{cc6n4Tf#%mZu^VFU=K0%QaW3=gfpBS1iakgx!G5nx&i9P-2F^Eody za({+iKu&O?c?w*XhG)!&Cg zfawTOXzfh}xLhv3JP-m*OMpTeFcIK);zNMx39zpSvp$9g3nr zfC#WcT9783hTwA|M1W}tu#fn9Cn4X75Pt!tBf!4ms|47$7kny01ek^ZhY;Uj!w8>6 zhyddk;1J>)d>Td%qz4@0!NA)Q`a##Y5+IinffdyyMU3Qw=}11;@`9lUeB)Rq*dEK| zW_&OlZvDyC9AGz&&+VvSn1Fn6JsVgGaLWaiV4DGRMK73Uz@Lqm0wxIvi3pe`Ab)&O z4Vd(R`Km7PX8*^R)qv^#k9r_D-2d(P!fNoN{&CV;a6gbQJqatw?@HG=xAlWdCg|wAjV`RfH>hw#SQIrNJ#pg@LH)z6u$;Y3pt1%-8PLq%Cl9A&N?sF9;)t-u>03mkWhy=6?j?_NzDg8eR}?-TU?Se(VLoY37R;1oO<7?g%*V zdUWWH@ZG{NT+g?wF|Z1wmtB|_gj>-zEpuT^O@q_WODG2O&`T%==UFe4)8M4t)~7(t z;3}uwOl>lm+|wnY*K2C(`>`l#B^`1+nNP;R0`)#0M7U*vm~t^S`o$ERO@FN(-J(;1 z_z)o^D87>#A>lauQ*FPqkd4HIgaZ*my5d8Ghz}70;``jT3$b^+TCEOcSzd+yZiu~I zv#}}$7~0A6{AvLfMG>0U`|xJ4fLh_%q035G*N9Jlml|If9m<>K|aWyu_P!t1d&3|x5Z z^#%&djQe*yv*GR7eBdP;T6rzP(=RLGcKmF4OKAR@U`TkaUxkVd>k&09nX(WY;h_fm z(7aCR2!JNd+2eUyzL8gq@wyPgY+$C*BJCAJ7NfknL`Nwlg2YAF$w>KrNUF|acY0Pb z+4w{CTkRp=E%$PDBDE&8BFg(ZY;T0bA^K$5`q@vPQ+=-f4MOTa`w!ht=DUf6Hmd*t N002ovPDHLkV1i6p4Cnv= diff --git a/examples/starting-shootemup/assets/Flat dark joystick thumb.png b/examples/starting-shootemup/assets/Flat dark joystick thumb.png index 36f5262a9055b15bf36ba5ddee957e1fa854561e..8acf179f8660be36ccea1198105da35c07154fd7 100644 GIT binary patch delta 2913 zcmV-n3!e0p2>TY0Bo78+OGiWi{{a60|De66laV18e*gz`Nliru>IW485iPkW{Hp)} z3kXR>K~#9!?Oj=H8`l**_stBok)rm+alOTHY)jTcOO_Pdau+1&5)^3yq%HEUA680M5oiMuZ2}=5KmkC$ zh2P)_Fvb}e2lHhHUl&5_kWyZ;W~Ms(dppL`v7MAICsJA4SY{Z;E@u8VY2tALtxxO` z_=1w^BNFrbCIX(RoBGKF1b<(^q1*oatr!n)hnx?%3qKDGS z{^p92xlEA9m_UsOu{x~uKjTlcg8Rl6VglBVki zf9I^`o&z&?O56HT&*_$9_mluzwpA7Q^Zmad63^p405iWiIehDdi_U#oq=U0J$D9~)svkkj~-4-0jlbobOG^Oq3O>9(2~|=iBMEr zioAlth3t%tVq|y-%$!USq*ChjnaI?Of9Lv6&!&w4J1T05vV6H80<=5Xd8MeN6!`^3 zi*6eq8^Or!p`<$!z~`qXLi^73caN_2$Hb~5KhRj2<@5IeRF`b>HBEy*uVBgT{=5Qc znwD$<2%zoRxp}=+b&ZuN6`;C)U$gGXI0B%=iDW1=4MHq^%0dWen(kC?F4y&pf1}lP z`x;XsKyAbRZ)uwLK7jnRs#w5I9xhMQwL^i%<|iB#Akf(SD3SOl0H2esm9~UZYUxW^ zr66tTwB|m5{#Dc1{FoyGRPWha3DCO$49CX`l*Gu$(&J_$!$VL?rlRBN0`yLG{l4m? zRzcPE`!;Ku)(OC$N}KT)6r!lGe;8se;YuknI&vGK@stnwhomiQyW5WqCR~8+I|6yx ze%}Cq;&kwOAq0e`f#w8=W=#%bWM(?Dy}!GCG@%xhmFqo}F64ja_qJuhvaEC>9{`Fx zS=sN#U0TOg@$A|E3JLKnT*PTBw6S#E2Hd)S^>FNcW1yyP?>?dHhZAwpf40V;Y-#W9 zX*=G!Vhi#E9@4ZwyX3F%x>0CvZ`t;6)`|squ%Y=kMD(D`7 zcY1xf-T3rIxr>#mhl-j#6t@7H$MZN5 zebMDEVOF$PDz1;qf+}kpe|3+?(+Q$Vm%0RkxzDn!n%<5UJ8BOQLNtJ=!euPs-C!qx z#;6vwqavW`y8af@Z_dtrA%SifuWhdgXiEy9XJl*u(H57hq~(G@me*Unv;ew(5P-*} zDrqp7Xs{p(%Pf0~&CK7p|zqdWKpa=_vcm3 zTaZ%xWmf=aZHQTZWnKUww6D9II154AH}4RD8eB>o?gB#W0U&5spt0WYWSnv-aUP~= z286B~?$AT3E{k=2f4cy|9d=F;7}zM7d9zDJsubXW0BF;)b#svzZiv?DL=@NDe#7HI zeqk|uzFgRrh2Z!oCMH7Q^qQ6Cd^JM^e_jFd3JRfV8Y1CIj1CXMvaEQ%TSB!B&9{jt z>XbvTFBfIy4}dr;df$nnNnzd}aL(NDt@{RWw?8_8Z8nx+K|!!V3haW_8{5e=~xKwFjtqO~`UM?|n~ zYgOERDgblTe_x4&C!@~4kAx?&RuPFrmfWV4!sJ9KRzI=?12fTfky2vl=JiEIFwI#E z58Yg|0^GiJ1Lh)8u9U*i&1*5OnHfPHzz|}rb_`EWU}k0-Ma8Aa@%kX8L~v{rq2Sn> z^#41{&8wF`MsBVjdHIFV4Fi*t6Br*Ij`!IWA)T3Te^VUK@v$unw{BdUyV`T_-P|cc zC^(K#aD3U^F!PWA^NnSuAD8t+al?E?5K)^;T*^wRLBYT^mx+|&jIb^9oXbOMU|ZHD z0)Rl{0e6f!1^K(9^{|+mmZID|;4qX@r{;$ul$0O2lsGQHJM#jNCO&|BtIJXQAwehR z1sES2f9nBYy1Y0iz;r0wJ1@Z1i~UndslzTW&Ol1}!NB>hiFpA4KoI@RwIOGsUkLH@ zdEP(!V=5dT1Tf|DlXOhYMy4*`CBV79j-ZtGKU{v2UH!=a_Md4VyGsB75JGzyfaTJZ zG?=!OKVHE3o&N-b!C_|Zba_ga`}3tpIC5i2e*p&1_eje$UvQIga_Bcr^ZSG6d*qS= z0Dx4gg_+N~d?h;gxu2D4`TXa132?Uilsz4ZJm>a;#KN-77yG(T*)atG00LSUQ_IB) zqhG1RmO1m`5?n8iZ54sdUZ3v*lK3uwwQ}Jbo(ydr=BCp8ZFJ5c^$ffBixVoPQ)TG5)o9_g>aiu3dFaTMXUP2_QF> zP0TAOKtW;2{0bB$B}PYw5gbc-e1t5^4D_CExfFLGE$7#J+gmP4DGvY$r;Pmk0u+~& zFIfH~G!4ZiWys4fNG17U+qU;Fhx}y=007->$GVhKPXVx+)%siXhnPepLFCun*cud&h{7a*)yJ=Jyc=%Ez_)k=@rdFtqU zwrxKK=8#h@rF5H_%^Z|cKGAjZ=zA;f)vGyvPy6u}(=wZxdB}S@jg^wpDe{L+ z%RJECc5GF%NmgCIo#|-p2?fWte>rgvK}v~WaD2(_!SOLj2Lxc|j>(DOLuWc#dsc0$ zi8!J0(c7~{W$XS3<{}|P07-oK-^^45qzPnYWiLF3u~CdgdB<3yV!*cTHW$G`LoZ*uIxw90!Xk;N3_Q}H>)M+_h^l*XOh>6MY1=>S?YPI@ zDSy`x#_pyc=(_O&-6KzCXXZv-R)EFRdNIuDT|TB|m3)JU=C(O-R4xmdwl_ zo9666A+$gBo<8A7J?l6Lf7>f+GlXue_he-JWv*h%N+sz4W|&ncKu=DECnwJLbSnvFKw%dgeWJX z4MfNTD36GIpEK|XfN^FHGV>Jz`bdZY%d#(;Gm#4e{oOaxv7P@1TUu73{ky})00000 LNkvXXu0mjflp>}T delta 1022 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 DzjMRo diff --git a/examples/starting-shootemup/assets/StartingEnemyBullet.png b/examples/starting-shootemup/assets/StartingEnemyBullet.png index 9994713d81ec0e5e015b6721c245f0d03eaf06bc..8590fa4dc35a0f0d2cb5a2306aac0e5c4f22837e 100644 GIT binary patch literal 1885 zcmV-j2cr0iP)pVWzz# zzUT~u3*ZSzpP=OllDt89g2EF3H)&>ifq8*iGyxhY)D)V~BqqXf97q09&skd@+gP&f zn3S3BH>0eitff6?&)=>9I2;a#!{Kl^91e%W;r!na-~*7HQe}@HR7rpk1c5_yAC4s7|Db=)V`w$ zj)Vf>^GGlf41j6E&F23AA?FHs5^zgDIdJ9#5-3 zpkfy4?}?HG{lg>BHxL4kzn@)OluIQj=5w&Oon}Wn#4^l8mbBaT_dXg0kk6CZ3sp;z z`uZR^ats1PM_m2BWEha$SZk*U9b-bQJ|=je2hiZD^_D<3rHTL+LJ4#?X$gJxK2(LL z87JZEXS*Gwu)%SKwb_5x?YhYnSh1~T9ak1$<#nOr&(Z2EsbvvlYsT7;!Xts&pKleB zF?G)1KxkkPJ~j^hM@HcwkR>=M0Wbl@&n#&PI=59oHUD0zMq(9uL&_|`wPuT0xvY}u ztHIGRHuJq;XZ;oIZf&&uAHJ(rd%UNBR)q(dq9Eddttfda*H!a0DG(X^7>xG7B|0i0 zZYd#&g%ig0B>6g-a5v=hdC<4tfiy8vjY#4ze2HCY`9O> z@M!rrh@LwK{e!Z-0!(J%3rqpQ;epw~i7%8+2SJeb(_5SHW_|s=!L))b;Y}t7pA80S zp|n7O)NQJ(hlF%`-}9$W;rut>vdL`e1|meMVG0QyaHWdf(Zw}P5Nv}u~=}`FOPw_ zomDdh6pefey5Kkl8}hZ$u`%#;U_k+IV_&;SZ3jhrH_({MWoNf!BhmkUd3$JFnROHG zo1wJBQcnb*qi;hdoraA!YfwaAr$E?@R}^6^)K9CxMDv^RUHwXqpEZRG{0sWu) z<&4$~CUiZPhM5KAjO@bO7tc~uN4L|M6X9?O222CS!vmlUh1gnu5?hS=@F@yShw@2? zWdigbYf+*Z*_}>a!)Er>*{Gw{Q7SZcckJr`oG)sb0%CVH-OTUDGp|yKjYkh*`{i@U zVHL$5;8G#fcl*UtDC{*BM1EZN_oK-V`uo@+Vgg3(r%o?bc5BcQ9^^1pb?OGeV30+5 zS#As^q?8kpldKMCvoN~H`de3#N{t?0xv0(<@ATRIjIPx8CikchDD~$CM%+?iA6%lB z1-NEipaL`!ei!Ta(AWfY0-1UA-1$nxN5zxy!BB{`G#;81TZ$DIbX*JIWL?D`7lhue z1$4|CaonqGSPS5e&3!7p!>9mS3HE|_uT!=bK>m+wff$|JI#G4CL`TDF5ET+d|5dP+ ztGhdH(i!d8-Yn)|q16s>$%J_k6_CLN3|Rv$iVgy;1f9&kT(=F^lMNATKfu#sCm170 z%4t*5!mV}o?R2APog=uIKFBP-Nm1tE-eoV9J3(L4Vm7%#-pbS;S;73B|DTTMmBp7h# zBTd$L=oWyceico97P<_q^~G8TnTPWJeDe-8ai|KYel<$M;i98m+&F9VmFQ4iz$|0t z==rA@hxcK*jUQ^hKjjdF58VZpr`0(e)b*y1n>~dDCx5p#KArf_vH%rYScp>)jM7&l zR#SAV6ijXSTaC(Usw~|IZt+ol8M2_l3;Zn#EZqBa^C_Lf;cz${4u`|xa5x+e$BBOd Xrzq2t?qQ+500000NkvXXu0mjf_HAO0 literal 6461 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 diff --git a/examples/starting-shootemup/assets/StartingObstacle.png b/examples/starting-shootemup/assets/StartingObstacle.png index b33b91d0a61ed68cb61c757ad236567504017ed3..a963c15fb01bfb514613d3a8beb2763128eef1d2 100644 GIT binary patch literal 429 zcmeAS@N?(olHy`uVBq!ia0vp^4Is?H1|$#LC7uRSY)RhkE)4%caKYZ?lYt_f1s;*b z3=G`DAk4@xYmNj^kiEpy*OmPhyBLqP?5poKlNcBntvp>ELn>~)y{pLA;vnMUn0BaF zK-+>t?5wRI$GIa~LOZxrjiXRS`Bb;!0SM0v#nEKNbw3DuFKT*v91fH&bF6*2UngIGpYBc}= delta 858 zcmV-g1Eu_}1Lp>RiBL{Q4GJ0x0000DNk~Le0000$0000$2nGNE0IF$m-T(jrgK0xU zP)S2WAaHVTW@&6?001bFeUUv#!$2IxUsI(j6^C{ZamY}etcZ#@N)?M>p|llRbuhW~ z3z{?}DK3tJYr(;f#j1mgv#t)Vf*|+-;x0HTx=4xtOA0N2VmvtR$GdxvyLW)ls4&&+ zngCSIGE(uFn9Z$$d%UY}?Hf!2b+fX>EVC z0nC1qUT@*Z%x0}MXtk|8;gpQcbVC;;zg^i4S+bPM#YxxKaaaryvc zsH^20;NTD#EmHQn$Gdwvd;9lHtG^#oh;o`>d{*<3Asl}$BLDy{BLR4&KXw2B00(qQ zO+^Rk2@?P^60GW9(*OVf8FWQhbVF}#ZDnqB07G(RVRU6=Aa`kWXdp*PO;A^X4i^9b z0YFJaK~#9!<4p)fSLH|0cPW;2C#si7QhOAN&riE^#IoJssVp2;?)9J#j6Ccj291J9WNTd170kE z7raOSPx#>hyy1ri@Q5E4z$<=80MGdD0N(Lk0T}Sj0a)-&0hsXB0od?W0T}Vc0a)=x z0hsZR0od`k0V3cp14P094G;-`3J?wd6Cfh~8z3qk0G7yj09Yd90bq%YFM5TU@s9!6 k@wXlUBH%9rM8N~V2QQ2ab2?$h;s5{u07*qoM6N<$f{ER5$^ZZW diff --git a/examples/starting-shootemup/assets/StartingPlayer.png b/examples/starting-shootemup/assets/StartingPlayer.png index c7dbe318947a7d46e540309fdfbc43c3d158c3c6..0ba05892c27e692f74ecc6720078c5f280e1dad7 100644 GIT binary patch delta 3391 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 diff --git a/examples/starting-shootemup/assets/starting_bulletfire.wav b/examples/starting-shootemup/assets/starting_bulletfire.wav new file mode 100644 index 0000000000000000000000000000000000000000..f7045dd18c0e6f5125fccbccfc36f5023656a915 GIT binary patch literal 6148 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 edhy=ZUaI$;!{ literal 0 HcmV?d00001 diff --git a/examples/starting-shootemup/starting-shootemup.json b/examples/starting-shootemup/starting-shootemup.json index 2d4a40cd0..a6714ad08 100644 --- a/examples/starting-shootemup/starting-shootemup.json +++ b/examples/starting-shootemup/starting-shootemup.json @@ -1,7 +1,7 @@ { "firstLayout": "", "gdVersion": { - "build": 272, + "build": 274, "major": 5, "minor": 6, "revision": 0 @@ -162,6 +162,34 @@ "name": "assets\\StartingObstacle.png", "smoothed": true, "userAdded": false + }, + { + "file": "assets/starting_bulletfire.wav", + "kind": "audio", + "metadata": "", + "name": "assets/starting_bulletfire.wav", + "preloadAsMusic": false, + "preloadAsSound": false, + "preloadInCache": false, + "userAdded": false + }, + { + "file": "assets/DirtParticle.png", + "kind": "image", + "metadata": "", + "name": "assets/DirtParticle.png", + "smoothed": true, + "userAdded": false + }, + { + "file": "assets/Collision.wav", + "kind": "audio", + "metadata": "", + "name": "assets/Collision.wav", + "preloadAsMusic": false, + "preloadAsSound": false, + "preloadInCache": false, + "userAdded": false } ] }, @@ -330,13 +358,13 @@ "adaptCollisionMaskAutomatically": false, "assetStoreId": "", "name": "Player", + "persistentUuid": "21148fd4-7988-4ee4-8974-dfb76d84b1cb", "type": "Sprite", "updateIfNotVisible": false, "variables": [], "effects": [], "behaviors": [ { - "isFolded": true, "name": "FireBullet", "type": "FireBullet::FireBullet", "FireCooldown": 0.2, @@ -366,6 +394,10 @@ "BulletLayer": "", "RandomizedAngle": 0 }, + { + "name": "ShakeObject_PositionAngle", + "type": "ShakeObject::ShakeObject_PositionAngle" + }, { "isFolded": true, "name": "TopDownMovement", @@ -531,6 +563,7 @@ "assetStoreId": "", "height": 352, "name": "RepeatingBackground", + "persistentUuid": "0381673b-62a0-4bce-8aea-f8db3e4243d5", "texture": "assets\\StartingTiledGround.png", "type": "TiledSpriteObject::TiledSprite", "width": 640, @@ -542,11 +575,13 @@ "adaptCollisionMaskAutomatically": true, "assetStoreId": "", "name": "Enemy", + "persistentUuid": "8db7e4f8-f356-43a6-b431-0c99120c408e", "type": "Sprite", "updateIfNotVisible": false, "variables": [ { "name": "Health", + "persistentUuid": "77f2f4c8-4055-4b21-bf58-0e4b89487e31", "type": "number", "value": 3 } @@ -582,6 +617,10 @@ "ExponentialCoolingRate": 0.3, "BulletLayer": "", "RandomizedAngle": 0 + }, + { + "name": "ShakeObject_PositionAngle", + "type": "ShakeObject::ShakeObject_PositionAngle" } ], "animations": [ @@ -636,9 +675,10 @@ ] }, { - "adaptCollisionMaskAutomatically": true, + "adaptCollisionMaskAutomatically": false, "assetStoreId": "", "name": "PlayerBullet", + "persistentUuid": "7907cec0-76a6-4e63-94ad-7a7a27477b97", "type": "Sprite", "updateIfNotVisible": false, "variables": [], @@ -666,31 +706,31 @@ "originPoint": { "name": "origine", "x": 0, - "y": 16 + "y": 32 }, "centerPoint": { "automatic": false, "name": "centre", "x": 0, - "y": 16 + "y": 32 }, "customCollisionMask": [ [ { - "x": 1, - "y": 5 + "x": 0, + "y": 14 }, { - "x": 32, - "y": 5 + "x": 50, + "y": 14 }, { - "x": 32, - "y": 27 + "x": 50, + "y": 50 }, { - "x": 1, - "y": 27 + "x": 0, + "y": 50 } ] ] @@ -702,9 +742,10 @@ ] }, { - "adaptCollisionMaskAutomatically": true, + "adaptCollisionMaskAutomatically": false, "assetStoreId": "", "name": "EnemyBullet", + "persistentUuid": "e7fa31b1-3998-4099-983a-68ad69a8e5fa", "type": "Sprite", "updateIfNotVisible": false, "variables": [], @@ -732,31 +773,31 @@ "originPoint": { "name": "origine", "x": 0, - "y": 16 + "y": 32 }, "centerPoint": { "automatic": false, "name": "centre", "x": 0, - "y": 16 + "y": 32 }, "customCollisionMask": [ [ { - "x": 1, - "y": 5 + "x": 0, + "y": 14 }, { - "x": 32, - "y": 5 + "x": 50, + "y": 14 }, { - "x": 32, - "y": 27 + "x": 50, + "y": 50 }, { - "x": 1, - "y": 27 + "x": 0, + "y": 50 } ] ] @@ -771,12 +812,109 @@ "assetStoreId": "", "height": 64, "name": "GameBoundary", + "persistentUuid": "4a1c4ef8-94e8-49b5-981d-9a8ba95a4d62", "texture": "assets\\StartingObstacle.png", "type": "TiledSpriteObject::TiledSprite", "width": 64, "variables": [], "effects": [], "behaviors": [] + }, + { + "additive": false, + "assetStoreId": "", + "destroyWhenNoParticles": true, + "emitterAngleA": 0, + "emitterAngleB": 360, + "emitterForceMax": 100, + "emitterForceMin": 50, + "flow": 800, + "jumpForwardInTimeOnCreation": 0.15, + "maxParticleNb": 300, + "name": "DebrisParticle", + "particleAlpha1": 255, + "particleAlpha2": 0, + "particleAlphaRandomness1": 0, + "particleAlphaRandomness2": 0, + "particleAngle1": 0, + "particleAngle2": 0, + "particleAngleRandomness1": 0, + "particleAngleRandomness2": 0, + "particleBlue1": 238, + "particleBlue2": 255, + "particleColor1": "238;238;238", + "particleColor2": "255;255;255", + "particleGravityX": 0, + "particleGravityY": 0, + "particleGreen1": 238, + "particleGreen2": 255, + "particleLifeTimeMax": 0.45, + "particleLifeTimeMin": 0.3, + "particleRed1": 238, + "particleRed2": 255, + "particleSize1": 50, + "particleSize2": 50, + "particleSizeRandomness1": 0, + "particleSizeRandomness2": 0, + "persistentUuid": "27e094e0-321c-4c94-b61c-3e24b76ccaf7", + "rendererParam1": 3, + "rendererParam2": 1, + "rendererType": "Quad", + "tank": 20, + "textureParticleName": "assets/DirtParticle.png", + "type": "ParticleSystem::ParticleEmitter", + "zoneRadius": 3, + "variables": [], + "effects": [], + "behaviors": [] + }, + { + "additive": false, + "assetStoreId": "", + "destroyWhenNoParticles": true, + "emitterAngleA": 0, + "emitterAngleB": 360, + "emitterForceMax": 150, + "emitterForceMin": 80, + "flow": 800, + "jumpForwardInTimeOnCreation": 0.15, + "maxParticleNb": 300, + "name": "LargeDebrisParticle", + "particleAlpha1": 255, + "particleAlpha2": 0, + "particleAlphaRandomness1": 0, + "particleAlphaRandomness2": 0, + "particleAngle1": 0, + "particleAngle2": 0, + "particleAngleRandomness1": 0, + "particleAngleRandomness2": 0, + "particleBlue1": 238, + "particleBlue2": 255, + "particleColor1": "238;238;238", + "particleColor2": "255;255;255", + "particleGravityX": 0, + "particleGravityY": 0, + "particleGreen1": 238, + "particleGreen2": 255, + "particleLifeTimeMax": 0.45, + "particleLifeTimeMin": 0.3, + "particleRed1": 238, + "particleRed2": 255, + "particleSize1": 100, + "particleSize2": 100, + "particleSizeRandomness1": 0, + "particleSizeRandomness2": 0, + "persistentUuid": "adc26203-7a31-49a7-b53f-969d2b4853d7", + "rendererParam1": 3, + "rendererParam2": 1, + "rendererType": "Quad", + "tank": 20, + "textureParticleName": "assets/DirtParticle.png", + "type": "ParticleSystem::ParticleEmitter", + "zoneRadius": 3, + "variables": [], + "effects": [], + "behaviors": [] } ], "objectsFolderStructure": { @@ -794,6 +932,12 @@ { "objectName": "EnemyBullet" }, + { + "objectName": "DebrisParticle" + }, + { + "objectName": "LargeDebrisParticle" + }, { "objectName": "RepeatingBackground" }, @@ -958,6 +1102,64 @@ } ] }, + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [ + { + "type": { + "value": "FireBullet::FireBullet::HasJustFired" + }, + "parameters": [ + "Player", + "FireBullet", + "" + ] + } + ], + "actions": [ + { + "type": { + "value": "PlaySound" + }, + "parameters": [ + "", + "assets/starting_bulletfire.wav", + "", + "20", + "RandomWithStep(0.9,1.2,0.05)" + ] + } + ] + }, + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [ + { + "type": { + "value": "FireBullet::FireBullet::HasJustFired" + }, + "parameters": [ + "Enemy", + "FireBullet", + "" + ] + } + ], + "actions": [ + { + "type": { + "value": "PlaySound" + }, + "parameters": [ + "", + "assets/starting_bulletfire.wav", + "", + "65", + "RandomWithStep(0.8,0.9,0.05)" + ] + } + ] + }, { "type": "BuiltinCommonInstructions::Standard", "conditions": [ @@ -975,6 +1177,18 @@ } ], "actions": [ + { + "type": { + "value": "Create" + }, + "parameters": [ + "", + "DebrisParticle", + "PlayerBullet.BoundingBoxRight()", + "PlayerBullet.CenterY()", + "" + ] + }, { "type": { "value": "SetNumberObjectVariable" @@ -986,6 +1200,18 @@ "1" ] }, + { + "type": { + "value": "PlaySound" + }, + "parameters": [ + "", + "assets/Collision.wav", + "", + "30", + "RandomWithStep(0.9,1.1,0.05)" + ] + }, { "type": { "value": "Delete" @@ -994,6 +1220,22 @@ "PlayerBullet", "" ] + }, + { + "type": { + "value": "ShakeObject::ShakeObject_PositionAngle::ShakeObject_PositionAngle" + }, + "parameters": [ + "Enemy", + "ShakeObject_PositionAngle", + "0.2", + "RandomWithStep(-5,5,10)", + "RandomWithStep(-5,5,10)", + "RandomWithStep(-5,5,10)", + "0.05", + "", + "" + ] } ] }, @@ -1021,6 +1263,18 @@ "Enemy", "" ] + }, + { + "type": { + "value": "Create" + }, + "parameters": [ + "", + "LargeDebrisParticle", + "Enemy.CenterX()", + "Enemy.CenterY()", + "" + ] } ] }, @@ -1038,9 +1292,81 @@ "", "" ] + }, + { + "type": { + "value": "BuiltinCommonInstructions::Once" + }, + "parameters": [] } ], "actions": [ + { + "type": { + "value": "ShakeObject::ShakeObject_PositionAngle::ShakeObject_PositionAngle" + }, + "parameters": [ + "Player", + "ShakeObject_PositionAngle", + "0.2", + "RandomWithStep(-5,5,10)", + "RandomWithStep(-5,5,10)", + "RandomWithStep(-5,5,10)", + "0.05", + "", + "" + ] + }, + { + "type": { + "value": "Create" + }, + "parameters": [ + "", + "LargeDebrisParticle", + "EnemyBullet.BoundingBoxLeft()", + "EnemyBullet.CenterY()", + "" + ] + }, + { + "type": { + "value": "PlaySound" + }, + "parameters": [ + "", + "assets/Collision.wav", + "", + "90", + "RandomFloatInRange(0.7,0.9)" + ] + }, + { + "type": { + "value": "Delete" + }, + "parameters": [ + "EnemyBullet", + "" + ] + }, + { + "type": { + "value": "ChangeTimeScale" + }, + "parameters": [ + "", + "0.15" + ] + }, + { + "type": { + "value": "Wait" + }, + "parameters": [ + "0.15" + ] + }, { "type": { "value": "Scene" @@ -1150,6 +1476,10 @@ "name": "Scale", "type": "ScalableCapability::ScalableBehavior" }, + { + "name": "ShakeObject_PositionAngle", + "type": "ShakeObject::ShakeObject_PositionAngle" + }, { "name": "TopDownMovement", "type": "TopDownMovementBehavior::TopDownMovementBehavior" @@ -15235,6 +15565,4479 @@ } ], "eventsBasedObjects": [] + }, + { + "author": "Tristan Rhodes (https://victrisgames.itch.io/)", + "category": "Visual effect", + "dimension": "2D", + "extensionNamespace": "", + "fullName": "Shake object", + "gdevelopVersion": ">=5.5.222", + "helpPath": "", + "iconUrl": "data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iVVRGLTgiPz48IURPQ1RZUEUgc3ZnIFBVQkxJQyAiLS8vVzNDLy9EVEQgU1ZHIDEuMS8vRU4iICJodHRwOi8vd3d3LnczLm9yZy9HcmFwaGljcy9TVkcvMS4xL0RURC9zdmcxMS5kdGQiPjxzdmcgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIiB4bWxuczp4bGluaz0iaHR0cDovL3d3dy53My5vcmcvMTk5OS94bGluayIgdmVyc2lvbj0iMS4xIiBpZD0ibWRpLWFycm93LWFsbCIgd2lkdGg9IjI0IiBoZWlnaHQ9IjI0IiB2aWV3Qm94PSIwIDAgMjQgMjQiPjxwYXRoIGQ9Ik0xMywxMUgxOEwxNi41LDkuNUwxNy45Miw4LjA4TDIxLjg0LDEyTDE3LjkyLDE1LjkyTDE2LjUsMTQuNUwxOCwxM0gxM1YxOEwxNC41LDE2LjVMMTUuOTIsMTcuOTJMMTIsMjEuODRMOC4wOCwxNy45Mkw5LjUsMTYuNUwxMSwxOFYxM0g2TDcuNSwxNC41TDYuMDgsMTUuOTJMMi4xNiwxMkw2LjA4LDguMDhMNy41LDkuNUw2LDExSDExVjZMOS41LDcuNUw4LjA4LDYuMDhMMTIsMi4xNkwxNS45Miw2LjA4TDE0LjUsNy41TDEzLDZWMTFaIiAvPjwvc3ZnPg==", + "name": "ShakeObject", + "previewIconUrl": "https://resources.gdevelop-app.com/assets/Icons/arrow-all.svg", + "shortDescription": "Shake objects with configurable translation, rotation, and scale intensity.", + "version": "1.6.2", + "description": [ + "Shake an object (position, angle or scale).", + "", + "It can be used for:", + "- Hit or impact", + "- Object slowly rocking back and forth (like a ship)", + "- Simulate engine vibration, earthquake, or pulsing", + "", + "The top-down RPG example uses this extension for damage animations ([open the project online](https://editor.gdevelop.io/?project=example://top-down-rpg))." + ], + "origin": { + "identifier": "ShakeObject", + "name": "gdevelop-extension-store" + }, + "tags": [ + "shaking", + "object", + "effect", + "shake", + "scale", + "position", + "rotate", + "angle" + ], + "authorIds": [ + "gqDaZjCfevOOxBYkK6zlhtZnXCg1" + ], + "dependencies": [], + "globalVariables": [], + "sceneVariables": [], + "eventsFunctions": [], + "eventsFunctionsFolderStructure": { + "folderName": "__ROOT" + }, + "eventsBasedBehaviors": [ + { + "description": "Shake objects with translation and rotation.", + "fullName": "Shake object (position, angle)", + "helpPath": "", + "iconUrl": "", + "name": "ShakeObject_PositionAngle", + "objectType": "", + "previewIconUrl": "", + "eventsFunctions": [ + { + "description": "Shake an object, using one or more ways to shake (position, angle). Make sure to \"Stop shaking\" before starting a new shake if it uses different parameters. ", + "fullName": "Shake object (position, angle)", + "functionType": "Action", + "name": "ShakeObject_PositionAngle", + "sentence": "Shake object _PARAM0_ for _PARAM2_ seconds. Modify position amplitude _PARAM3_ on X axis and _PARAM4_ on Y axis, angle rotation amplitude _PARAM5_. Wait _PARAM6_ seconds between shakes. Keep shaking until stopped: _PARAM7_", + "events": [ + { + "colorB": 228, + "colorG": 176, + "colorR": 74, + "creationTime": 0, + "name": "Shake Object", + "source": "", + "type": "BuiltinCommonInstructions::Group", + "events": [ + { + "type": "BuiltinCommonInstructions::Comment", + "color": { + "b": 109, + "g": 230, + "r": 255, + "textB": 0, + "textG": 0, + "textR": 0 + }, + "comment": "Start/Reset duration timer" + }, + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [], + "actions": [ + { + "type": { + "value": "ResetObjectTimer" + }, + "parameters": [ + "Object", + "\"__ShakeObject_DurationTimer\"" + ] + } + ] + }, + { + "type": "BuiltinCommonInstructions::Comment", + "color": { + "b": 109, + "g": 230, + "r": 255, + "textB": 0, + "textG": 0, + "textR": 0 + }, + "comment": "Pass input parameters to global variables so that onScenePostEvents can use them" + }, + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [], + "actions": [ + { + "type": { + "value": "ModVarObjet" + }, + "parameters": [ + "Object", + "__ShakeObject_Duration", + "=", + "Duration" + ] + }, + { + "type": { + "value": "ModVarObjet" + }, + "parameters": [ + "Object", + "__ShakeObject_PowerX", + "=", + "PowerX" + ] + }, + { + "type": { + "value": "ModVarObjet" + }, + "parameters": [ + "Object", + "__ShakeObject_PowerY", + "=", + "PowerY" + ] + }, + { + "type": { + "value": "ModVarObjet" + }, + "parameters": [ + "Object", + "__ShakeObject_PowerAngle", + "=", + "PowerAngle" + ] + }, + { + "type": { + "value": "ModVarObjet" + }, + "parameters": [ + "Object", + "__ShakeObject_TimeBetweenShakes", + "=", + "TimeBetweenShakes" + ] + } + ] + }, + { + "type": "BuiltinCommonInstructions::Comment", + "color": { + "b": 109, + "g": 230, + "r": 255, + "textB": 0, + "textG": 0, + "textR": 0 + }, + "comment": "Determine if the shake should keep going until stopped" + }, + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [], + "actions": [ + { + "type": { + "value": "SetObjectVariableAsBoolean" + }, + "parameters": [ + "Object", + "__ShakeObject_ShakeForever", + "False" + ] + } + ] + }, + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [ + { + "type": { + "value": "BooleanVariable" + }, + "parameters": [ + "ShakeForever", + "True", + "" + ] + } + ], + "actions": [ + { + "type": { + "value": "SetObjectVariableAsBoolean" + }, + "parameters": [ + "Object", + "__ShakeObject_ShakeForever", + "True" + ] + } + ] + }, + { + "type": "BuiltinCommonInstructions::Comment", + "color": { + "b": 109, + "g": 230, + "r": 255, + "textB": 0, + "textG": 0, + "textR": 0 + }, + "comment": "Add default values if none were provided" + }, + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [ + { + "type": { + "value": "VarObjet" + }, + "parameters": [ + "Object", + "__ShakeObject_Duration", + "=", + "0" + ] + } + ], + "actions": [ + { + "type": { + "value": "ModVarObjet" + }, + "parameters": [ + "Object", + "__ShakeObject_Duration", + "=", + "0.5" + ] + } + ] + }, + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [ + { + "type": { + "value": "VarObjet" + }, + "parameters": [ + "Object", + "__ShakeObject_TimeBetweenShakes", + "=", + "0" + ] + } + ], + "actions": [ + { + "type": { + "value": "ModVarObjet" + }, + "parameters": [ + "Object", + "__ShakeObject_TimeBetweenShakes", + "=", + "0.08" + ] + } + ] + }, + { + "type": "BuiltinCommonInstructions::Comment", + "color": { + "b": 109, + "g": 230, + "r": 255, + "textB": 0, + "textG": 0, + "textR": 0 + }, + "comment": "If duration is less than a single shake, increase duration to make 1 full shake" + }, + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [ + { + "type": { + "value": "VarObjet" + }, + "parameters": [ + "Object", + "__ShakeObject_Duration", + "<", + "Object.Variable(__ShakeObject_TimeBetweenShakes)" + ] + } + ], + "actions": [ + { + "type": { + "value": "ModVarObjet" + }, + "parameters": [ + "Object", + "__ShakeObject_Duration", + "=", + "Object.Variable(__ShakeObject_TimeBetweenShakes)" + ] + } + ] + }, + { + "type": "BuiltinCommonInstructions::Comment", + "color": { + "b": 109, + "g": 230, + "r": 255, + "textB": 0, + "textG": 0, + "textR": 0 + }, + "comment": "Detect initial shake" + }, + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [ + { + "type": { + "value": "VarObjet" + }, + "parameters": [ + "Object", + "__ShakeObject_ShakeInProgress", + "=", + "0" + ] + } + ], + "actions": [ + { + "type": { + "value": "ModVarObjet" + }, + "parameters": [ + "Object", + "__ShakeObject_InitialShake", + "=", + "1" + ] + } + ] + }, + { + "type": "BuiltinCommonInstructions::Comment", + "color": { + "b": 109, + "g": 230, + "r": 255, + "textB": 0, + "textG": 0, + "textR": 0 + }, + "comment": "Initiate the onScenePostEvents function" + }, + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [], + "actions": [ + { + "type": { + "value": "ModVarObjet" + }, + "parameters": [ + "Object", + "__ShakeObject_ShakeInProgress", + "=", + "1" + ] + } + ] + } + ], + "parameters": [] + } + ], + "parameters": [ + { + "description": "Object", + "name": "Object", + "type": "object" + }, + { + "description": "Behavior", + "name": "Behavior", + "supplementaryInformation": "ShakeObject::ShakeObject_PositionAngle", + "type": "behavior" + }, + { + "description": "Duration of shake (in seconds) (Default: 0.5) ", + "name": "Duration", + "type": "expression" + }, + { + "description": "Amplitude of postion shake in X direction (in pixels) (For example: 5)", + "name": "PowerX", + "type": "expression" + }, + { + "description": "Amplitude of position shake in Y direction (in pixels) (For example: 5)", + "longDescription": "Use a negative number to make the single-shake move in the opposite direction.", + "name": "PowerY", + "type": "expression" + }, + { + "description": "Amplitude of angle rotation shake (in degrees) (For example: 5)", + "name": "PowerAngle", + "type": "expression" + }, + { + "description": "Amount of time between shakes (in seconds) (Default: 0.08)", + "longDescription": "For a single-shake effect, set it to the same value as \"Duration\".", + "name": "TimeBetweenShakes", + "type": "expression" + }, + { + "description": "Keep shaking until stopped", + "longDescription": "Duration value will be ignored", + "name": "ShakeForever", + "type": "yesorno" + } + ], + "objectGroups": [] + }, + { + "fullName": "", + "functionType": "Action", + "name": "doStepPostEvents", + "sentence": "", + "events": [ + { + "colorB": 228, + "colorG": 176, + "colorR": 74, + "creationTime": 0, + "name": "Shake Object", + "source": "", + "type": "BuiltinCommonInstructions::Group", + "events": [ + { + "colorB": 228, + "colorG": 176, + "colorR": 74, + "creationTime": 0, + "name": "Start shaking", + "source": "", + "type": "BuiltinCommonInstructions::Group", + "events": [ + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [ + { + "type": { + "value": "VarObjet" + }, + "parameters": [ + "Object", + "__ShakeObject_ShakeInProgress", + "=", + "1" + ] + } + ], + "actions": [], + "events": [ + { + "type": "BuiltinCommonInstructions::Comment", + "color": { + "b": 109, + "g": 230, + "r": 255, + "textB": 0, + "textG": 0, + "textR": 0 + }, + "comment": "Keep object shaking forever (if desired)" + }, + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [ + { + "type": { + "value": "ObjectVariableAsBoolean" + }, + "parameters": [ + "Object", + "__ShakeObject_ShakeForever", + "True" + ] + } + ], + "actions": [ + { + "type": { + "value": "ModVarObjet" + }, + "parameters": [ + "Object", + "__ShakeObject_Duration", + "=", + "100" + ] + }, + { + "type": { + "value": "ResetObjectTimer" + }, + "parameters": [ + "Object", + "\"__ShakeObject_DurationTimer\"" + ] + } + ] + }, + { + "colorB": 228, + "colorG": 176, + "colorR": 74, + "creationTime": 0, + "name": "Calculate movement of the shake", + "source": "", + "type": "BuiltinCommonInstructions::Group", + "events": [ + { + "type": "BuiltinCommonInstructions::Comment", + "color": { + "b": 109, + "g": 230, + "r": 255, + "textB": 0, + "textG": 0, + "textR": 0 + }, + "comment": "Run once before every shake movement" + }, + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [ + { + "type": { + "value": "BuiltinCommonInstructions::Or" + }, + "parameters": [], + "subInstructions": [ + { + "type": { + "value": "VarObjet" + }, + "parameters": [ + "Object", + "__ShakeObject_InitialShake", + "=", + "1" + ] + }, + { + "type": { + "value": "ObjectTimer" + }, + "parameters": [ + "Object", + "\"__ShakeObject_ShakeTimer\"", + "Object.Variable(__ShakeObject_TimeBetweenShakes)" + ] + } + ] + } + ], + "actions": [ + { + "type": { + "value": "ResetObjectTimer" + }, + "parameters": [ + "Object", + "\"__ShakeObject_ShakeTimer\"" + ] + }, + { + "type": { + "value": "ModVarObjet" + }, + "parameters": [ + "Object", + "__ShakeObject_ShakeCounter", + "+", + "1" + ] + } + ], + "events": [ + { + "colorB": 228, + "colorG": 176, + "colorR": 74, + "creationTime": 0, + "name": "Correct for drift and reset drift tracking variables", + "source": "", + "type": "BuiltinCommonInstructions::Group", + "events": [ + { + "type": "BuiltinCommonInstructions::Comment", + "color": { + "b": 109, + "g": 230, + "r": 255, + "textB": 0, + "textG": 0, + "textR": 0 + }, + "comment": "Move to correct any drift from previous shake" + }, + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [ + { + "type": { + "value": "BuiltinCommonInstructions::Or" + }, + "parameters": [], + "subInstructions": [ + { + "type": { + "value": "VarObjet" + }, + "parameters": [ + "Object", + "__ShakeObject_PowerX", + "!=", + "0" + ] + }, + { + "type": { + "value": "VarObjet" + }, + "parameters": [ + "Object", + "__ShakeObject_PowerY", + "!=", + "0" + ] + } + ] + } + ], + "actions": [ + { + "type": { + "value": "SetX" + }, + "parameters": [ + "Object", + "-", + "Object.Variable(__ShakeObject_DisplacementTravelledX)" + ] + }, + { + "type": { + "value": "SetY" + }, + "parameters": [ + "Object", + "-", + "Object.Variable(__ShakeObject_DisplacementTravelledY)" + ] + } + ] + }, + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [ + { + "type": { + "value": "VarObjet" + }, + "parameters": [ + "Object", + "__ShakeObject_PowerAngle", + "!=", + "0" + ] + } + ], + "actions": [ + { + "type": { + "value": "SetAngle" + }, + "parameters": [ + "Object", + "-", + "Object.Variable(__ShakeObject_AngleTravelled)" + ] + } + ] + }, + { + "type": "BuiltinCommonInstructions::Comment", + "color": { + "b": 109, + "g": 230, + "r": 255, + "textB": 0, + "textG": 0, + "textR": 0 + }, + "comment": "Reset drift detection variables" + }, + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [], + "actions": [ + { + "type": { + "value": "ModVarObjet" + }, + "parameters": [ + "Object", + "__ShakeObject_DisplacementTravelledX", + "=", + "0" + ] + }, + { + "type": { + "value": "ModVarObjet" + }, + "parameters": [ + "Object", + "__ShakeObject_DisplacementTravelledY", + "=", + "0" + ] + }, + { + "type": { + "value": "ModVarObjet" + }, + "parameters": [ + "Object", + "__ShakeObject_AngleTravelled", + "=", + "0" + ] + } + ] + } + ], + "parameters": [] + }, + { + "type": "BuiltinCommonInstructions::Comment", + "color": { + "b": 109, + "g": 230, + "r": 255, + "textB": 0, + "textG": 0, + "textR": 0 + }, + "comment": "Calculate Position Shake" + }, + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [ + { + "type": { + "value": "BuiltinCommonInstructions::Or" + }, + "parameters": [], + "subInstructions": [ + { + "type": { + "value": "VarObjet" + }, + "parameters": [ + "Object", + "__ShakeObject_PowerX", + "!=", + "0" + ] + }, + { + "type": { + "value": "VarObjet" + }, + "parameters": [ + "Object", + "__ShakeObject_PowerY", + "!=", + "0" + ] + } + ] + } + ], + "actions": [], + "events": [ + { + "type": "BuiltinCommonInstructions::Comment", + "color": { + "b": 109, + "g": 230, + "r": 255, + "textB": 0, + "textG": 0, + "textR": 0 + }, + "comment": "PositionDisplacement = (DesiredDuration - RunningTimer) / DesiredDuration * Amplitude * [-1 or 1]" + }, + { + "type": "BuiltinCommonInstructions::Comment", + "color": { + "b": 109, + "g": 230, + "r": 255, + "textB": 0, + "textG": 0, + "textR": 0 + }, + "comment": "Make initial shake NOT random so users can set a direction for a one-shake effect" + }, + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [ + { + "type": { + "value": "VarObjet" + }, + "parameters": [ + "Object", + "__ShakeObject_InitialShake", + "=", + "1" + ] + } + ], + "actions": [], + "events": [ + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [ + { + "type": { + "value": "VarObjet" + }, + "parameters": [ + "Object", + "__ShakeObject_PowerX", + "!=", + "0" + ] + } + ], + "actions": [ + { + "type": { + "value": "ModVarObjet" + }, + "parameters": [ + "Object", + "__ShakeObject_DisplacementX", + "=", + "(Object.Variable(__ShakeObject_Duration) - Object.ObjectTimerElapsedTime(\"__ShakeObject_DurationTimer\")) / Object.Variable(__ShakeObject_Duration) * Object.Variable(__ShakeObject_PowerX)" + ] + } + ] + }, + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [ + { + "type": { + "value": "VarObjet" + }, + "parameters": [ + "Object", + "__ShakeObject_PowerY", + "!=", + "0" + ] + } + ], + "actions": [ + { + "type": { + "value": "ModVarObjet" + }, + "parameters": [ + "Object", + "__ShakeObject_DisplacementY", + "=", + "(Object.Variable(__ShakeObject_Duration) - Object.ObjectTimerElapsedTime(\"__ShakeObject_DurationTimer\")) / Object.Variable(__ShakeObject_Duration) * Object.Variable(__ShakeObject_PowerY)" + ] + } + ] + } + ] + }, + { + "type": "BuiltinCommonInstructions::Comment", + "color": { + "b": 109, + "g": 230, + "r": 255, + "textB": 0, + "textG": 0, + "textR": 0 + }, + "comment": "After initial shake pick a random direction" + }, + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [ + { + "type": { + "value": "VarObjet" + }, + "parameters": [ + "Object", + "__ShakeObject_InitialShake", + "=", + "0" + ] + } + ], + "actions": [], + "events": [ + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [ + { + "type": { + "value": "VarObjet" + }, + "parameters": [ + "Object", + "__ShakeObject_PowerX", + "!=", + "0" + ] + } + ], + "actions": [ + { + "type": { + "value": "ModVarObjet" + }, + "parameters": [ + "Object", + "__ShakeObject_DisplacementX", + "=", + "(Object.Variable(__ShakeObject_Duration) - Object.ObjectTimerElapsedTime(\"__ShakeObject_DurationTimer\")) / Object.Variable(__ShakeObject_Duration) * Object.Variable(__ShakeObject_PowerX) * RandomWithStep(-1, 1, 2)" + ] + } + ] + }, + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [ + { + "type": { + "value": "VarObjet" + }, + "parameters": [ + "Object", + "__ShakeObject_PowerY", + "!=", + "0" + ] + } + ], + "actions": [ + { + "type": { + "value": "ModVarObjet" + }, + "parameters": [ + "Object", + "__ShakeObject_DisplacementY", + "=", + "(Object.Variable(__ShakeObject_Duration) - Object.ObjectTimerElapsedTime(\"__ShakeObject_DurationTimer\")) / Object.Variable(__ShakeObject_Duration) * Object.Variable(__ShakeObject_PowerY) * RandomWithStep(-1, 1, 2)" + ] + } + ] + } + ] + } + ] + }, + { + "type": "BuiltinCommonInstructions::Comment", + "color": { + "b": 109, + "g": 230, + "r": 255, + "textB": 0, + "textG": 0, + "textR": 0 + }, + "comment": "Calculate Rotation (angle) shake " + }, + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [ + { + "type": { + "value": "VarObjet" + }, + "parameters": [ + "Object", + "__ShakeObject_PowerAngle", + "!=", + "0" + ] + } + ], + "actions": [], + "events": [ + { + "type": "BuiltinCommonInstructions::Comment", + "color": { + "b": 109, + "g": 230, + "r": 255, + "textB": 0, + "textG": 0, + "textR": 0 + }, + "comment": "Every \"even\" shake, rotate clockwise" + }, + { + "type": "BuiltinCommonInstructions::Comment", + "color": { + "b": 109, + "g": 230, + "r": 255, + "textB": 0, + "textG": 0, + "textR": 0 + }, + "comment": "AngleDisplacement = (DesiredDuration - RunningTimer) / DesiredDuration * Amplitude" + }, + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [ + { + "type": { + "value": "BuiltinCommonInstructions::CompareNumbers" + }, + "parameters": [ + "mod(Object.Variable(__ShakeObject_ShakeCounter),2)", + "=", + "0" + ] + } + ], + "actions": [ + { + "type": { + "value": "ModVarObjet" + }, + "parameters": [ + "Object", + "__ShakeObject_DisplacementAngle", + "=", + "(Object.Variable(__ShakeObject_Duration) - Object.ObjectTimerElapsedTime(\"__ShakeObject_DurationTimer\")) / Object.Variable(__ShakeObject_Duration) * Object.Variable(__ShakeObject_PowerAngle)" + ] + } + ] + }, + { + "type": "BuiltinCommonInstructions::Comment", + "color": { + "b": 109, + "g": 230, + "r": 255, + "textB": 0, + "textG": 0, + "textR": 0 + }, + "comment": "Every \"odd\" shake, rotate counter-clockwise" + }, + { + "type": "BuiltinCommonInstructions::Comment", + "color": { + "b": 109, + "g": 230, + "r": 255, + "textB": 0, + "textG": 0, + "textR": 0 + }, + "comment": "AngleDisplacement = (DesiredDuration - RunningTimer) / DesiredDuration * Amplitude" + }, + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [ + { + "type": { + "value": "BuiltinCommonInstructions::CompareNumbers" + }, + "parameters": [ + "mod(Object.Variable(__ShakeObject_ShakeCounter),2)", + "=", + "1" + ] + } + ], + "actions": [ + { + "type": { + "value": "ModVarObjet" + }, + "parameters": [ + "Object", + "__ShakeObject_DisplacementAngle", + "=", + "-1 *(Object.Variable(__ShakeObject_Duration) - Object.ObjectTimerElapsedTime(\"__ShakeObject_DurationTimer\")) / Object.Variable(__ShakeObject_Duration) * Object.Variable(__ShakeObject_PowerAngle)" + ] + } + ] + } + ] + }, + { + "type": "BuiltinCommonInstructions::Comment", + "color": { + "b": 109, + "g": 230, + "r": 255, + "textB": 0, + "textG": 0, + "textR": 0 + }, + "comment": "Save that initial shake has been processed" + }, + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [], + "actions": [ + { + "type": { + "value": "ModVarObjet" + }, + "parameters": [ + "Object", + "__ShakeObject_InitialShake", + "=", + "0" + ] + } + ] + } + ] + } + ], + "parameters": [] + }, + { + "colorB": 228, + "colorG": 176, + "colorR": 74, + "creationTime": 0, + "name": "Move object", + "source": "", + "type": "BuiltinCommonInstructions::Group", + "events": [ + { + "type": "BuiltinCommonInstructions::Comment", + "color": { + "b": 109, + "g": 230, + "r": 255, + "textB": 0, + "textG": 0, + "textR": 0 + }, + "comment": "Calculate the fraction of shake that occured during this frame" + }, + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [], + "actions": [ + { + "type": { + "value": "ModVarObjet" + }, + "parameters": [ + "Object", + "__ShakeObject_PercentTimeElapsedThisFrame", + "=", + "min(1,TimeDelta()/Object.Variable(__ShakeObject_TimeBetweenShakes))" + ] + } + ] + }, + { + "type": "BuiltinCommonInstructions::Comment", + "color": { + "b": 109, + "g": 230, + "r": 255, + "textB": 0, + "textG": 0, + "textR": 0 + }, + "comment": "Increase change for the first half of the shake (move away from original values)" + }, + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [ + { + "type": { + "inverted": true, + "value": "ObjectTimer" + }, + "parameters": [ + "Object", + "\"__ShakeObject_ShakeTimer\"", + "Object.Variable(__ShakeObject_TimeBetweenShakes)/2" + ] + } + ], + "actions": [], + "events": [ + { + "type": "BuiltinCommonInstructions::Comment", + "color": { + "b": 109, + "g": 230, + "r": 255, + "textB": 0, + "textG": 0, + "textR": 0 + }, + "comment": "Change position" + }, + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [ + { + "type": { + "value": "BuiltinCommonInstructions::Or" + }, + "parameters": [], + "subInstructions": [ + { + "type": { + "value": "VarObjet" + }, + "parameters": [ + "Object", + "__ShakeObject_PowerX", + "!=", + "0" + ] + }, + { + "type": { + "value": "VarObjet" + }, + "parameters": [ + "Object", + "__ShakeObject_PowerY", + "!=", + "0" + ] + } + ] + } + ], + "actions": [ + { + "type": { + "value": "SetX" + }, + "parameters": [ + "Object", + "+", + "round(1024 * Object.Variable(__ShakeObject_DisplacementX) * Object.Variable(__ShakeObject_PercentTimeElapsedThisFrame)) / 1024" + ] + }, + { + "type": { + "value": "SetY" + }, + "parameters": [ + "Object", + "+", + "round(1024 * Object.Variable(__ShakeObject_DisplacementY) * Object.Variable(__ShakeObject_PercentTimeElapsedThisFrame)) / 1024" + ] + } + ], + "events": [ + { + "type": "BuiltinCommonInstructions::Comment", + "color": { + "b": 109, + "g": 230, + "r": 255, + "textB": 0, + "textG": 0, + "textR": 0 + }, + "comment": "Save movement to calculate drift" + }, + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [], + "actions": [ + { + "type": { + "value": "ModVarObjet" + }, + "parameters": [ + "Object", + "__ShakeObject_DisplacementTravelledX", + "+", + "round(1024 * Object.Variable(__ShakeObject_DisplacementX) * Object.Variable(__ShakeObject_PercentTimeElapsedThisFrame)) / 1024" + ] + }, + { + "type": { + "value": "ModVarObjet" + }, + "parameters": [ + "Object", + "__ShakeObject_DisplacementTravelledY", + "+", + "round(1024 * Object.Variable(__ShakeObject_DisplacementY) * Object.Variable(__ShakeObject_PercentTimeElapsedThisFrame)) / 1024" + ] + } + ] + } + ] + }, + { + "type": "BuiltinCommonInstructions::Comment", + "color": { + "b": 109, + "g": 230, + "r": 255, + "textB": 0, + "textG": 0, + "textR": 0 + }, + "comment": "Change angle" + }, + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [ + { + "type": { + "value": "BuiltinCommonInstructions::CompareNumbers" + }, + "parameters": [ + "Object.Variable(__ShakeObject_PowerAngle)", + "!=", + "0" + ] + } + ], + "actions": [ + { + "type": { + "value": "SetAngle" + }, + "parameters": [ + "Object", + "+", + "round(1024 * Object.Variable(__ShakeObject_DisplacementAngle) * Object.Variable(__ShakeObject_PercentTimeElapsedThisFrame)) / 1024" + ] + } + ], + "events": [ + { + "type": "BuiltinCommonInstructions::Comment", + "color": { + "b": 109, + "g": 230, + "r": 255, + "textB": 0, + "textG": 0, + "textR": 0 + }, + "comment": "Save movement to calculate drift" + }, + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [], + "actions": [ + { + "type": { + "value": "ModVarObjet" + }, + "parameters": [ + "Object", + "__ShakeObject_AngleTravelled", + "+", + "round(1024 * Object.Variable(__ShakeObject_DisplacementAngle) * Object.Variable(__ShakeObject_PercentTimeElapsedThisFrame)) / 1024" + ] + } + ] + } + ] + } + ] + }, + { + "type": "BuiltinCommonInstructions::Comment", + "color": { + "b": 109, + "g": 230, + "r": 255, + "textB": 0, + "textG": 0, + "textR": 0 + }, + "comment": "Decrease change the second half of the shake (return to original position)" + }, + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [ + { + "type": { + "value": "ObjectTimer" + }, + "parameters": [ + "Object", + "\"__ShakeObject_ShakeTimer\"", + "Object.Variable(__ShakeObject_TimeBetweenShakes)/2" + ] + } + ], + "actions": [], + "events": [ + { + "type": "BuiltinCommonInstructions::Comment", + "color": { + "b": 109, + "g": 230, + "r": 255, + "textB": 0, + "textG": 0, + "textR": 0 + }, + "comment": "Change position" + }, + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [ + { + "type": { + "value": "BuiltinCommonInstructions::Or" + }, + "parameters": [], + "subInstructions": [ + { + "type": { + "value": "VarObjet" + }, + "parameters": [ + "Object", + "__ShakeObject_PowerX", + "!=", + "0" + ] + }, + { + "type": { + "value": "VarObjet" + }, + "parameters": [ + "Object", + "__ShakeObject_PowerY", + "!=", + "0" + ] + } + ] + } + ], + "actions": [ + { + "type": { + "value": "SetX" + }, + "parameters": [ + "Object", + "-", + "round(1024 * Object.Variable(__ShakeObject_DisplacementX) * Object.Variable(__ShakeObject_PercentTimeElapsedThisFrame)) / 1024" + ] + }, + { + "type": { + "value": "SetY" + }, + "parameters": [ + "Object", + "-", + "round(1024 * Object.Variable(__ShakeObject_DisplacementY) * Object.Variable(__ShakeObject_PercentTimeElapsedThisFrame)) / 1024" + ] + } + ], + "events": [ + { + "type": "BuiltinCommonInstructions::Comment", + "color": { + "b": 109, + "g": 230, + "r": 255, + "textB": 0, + "textG": 0, + "textR": 0 + }, + "comment": "Save movement to calculate drift" + }, + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [], + "actions": [ + { + "type": { + "value": "ModVarObjet" + }, + "parameters": [ + "Object", + "__ShakeObject_DisplacementTravelledX", + "-", + "round(1024 * Object.Variable(__ShakeObject_DisplacementX) * Object.Variable(__ShakeObject_PercentTimeElapsedThisFrame)) / 1024" + ] + }, + { + "type": { + "value": "ModVarObjet" + }, + "parameters": [ + "Object", + "__ShakeObject_DisplacementTravelledY", + "-", + "round(1024 * Object.Variable(__ShakeObject_DisplacementY) * Object.Variable(__ShakeObject_PercentTimeElapsedThisFrame)) / 1024" + ] + } + ] + } + ] + }, + { + "type": "BuiltinCommonInstructions::Comment", + "color": { + "b": 109, + "g": 230, + "r": 255, + "textB": 0, + "textG": 0, + "textR": 0 + }, + "comment": "Change angle" + }, + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [ + { + "type": { + "value": "BuiltinCommonInstructions::CompareNumbers" + }, + "parameters": [ + "Object.Variable(__ShakeObject_PowerAngle)", + "!=", + "0" + ] + } + ], + "actions": [ + { + "type": { + "value": "SetAngle" + }, + "parameters": [ + "Object", + "-", + "round(1024 * Object.Variable(__ShakeObject_DisplacementAngle) * Object.Variable(__ShakeObject_PercentTimeElapsedThisFrame)) / 1024" + ] + } + ], + "events": [ + { + "type": "BuiltinCommonInstructions::Comment", + "color": { + "b": 109, + "g": 230, + "r": 255, + "textB": 0, + "textG": 0, + "textR": 0 + }, + "comment": "Save movement to calculate drift" + }, + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [], + "actions": [ + { + "type": { + "value": "ModVarObjet" + }, + "parameters": [ + "Object", + "__ShakeObject_AngleTravelled", + "-", + "round(1024 * Object.Variable(__ShakeObject_DisplacementAngle) * Object.Variable(__ShakeObject_PercentTimeElapsedThisFrame)) / 1024" + ] + } + ] + } + ] + } + ] + } + ], + "parameters": [] + } + ] + } + ], + "parameters": [] + }, + { + "colorB": 228, + "colorG": 176, + "colorR": 74, + "creationTime": 0, + "name": "Stop shaking", + "source": "", + "type": "BuiltinCommonInstructions::Group", + "events": [ + { + "type": "BuiltinCommonInstructions::Comment", + "color": { + "b": 109, + "g": 230, + "r": 255, + "textB": 0, + "textG": 0, + "textR": 0 + }, + "comment": "Stop shaking when the duration has been reached (or if the stop shaking function was called)" + }, + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [ + { + "type": { + "value": "BuiltinCommonInstructions::Or" + }, + "parameters": [], + "subInstructions": [ + { + "type": { + "value": "ObjectTimer" + }, + "parameters": [ + "Object", + "\"__ShakeObject_DurationTimer\"", + "Object.Variable(__ShakeObject_Duration)" + ] + }, + { + "type": { + "value": "VarObjet" + }, + "parameters": [ + "Object", + "__ShakeObject_ShakeInProgress", + "=", + "-1" + ] + } + ] + }, + { + "type": { + "value": "BuiltinCommonInstructions::Once" + }, + "parameters": [] + } + ], + "actions": [ + { + "type": { + "value": "ModVarObjet" + }, + "parameters": [ + "Object", + "__ShakeObject_ShakeInProgress", + "=", + "0" + ] + }, + { + "type": { + "value": "ModVarObjet" + }, + "parameters": [ + "Object", + "__ShakeObject_ShakeCounter", + "=", + "0" + ] + } + ], + "events": [ + { + "colorB": 228, + "colorG": 176, + "colorR": 74, + "creationTime": 0, + "name": "Correct for drift and reset drift tracking variables", + "source": "", + "type": "BuiltinCommonInstructions::Group", + "events": [ + { + "type": "BuiltinCommonInstructions::Comment", + "color": { + "b": 109, + "g": 230, + "r": 255, + "textB": 0, + "textG": 0, + "textR": 0 + }, + "comment": "Move to correct any drift from previous shake" + }, + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [ + { + "type": { + "value": "BuiltinCommonInstructions::Or" + }, + "parameters": [], + "subInstructions": [ + { + "type": { + "value": "VarObjet" + }, + "parameters": [ + "Object", + "__ShakeObject_PowerX", + "!=", + "0" + ] + }, + { + "type": { + "value": "VarObjet" + }, + "parameters": [ + "Object", + "__ShakeObject_PowerY", + "!=", + "0" + ] + } + ] + } + ], + "actions": [ + { + "type": { + "value": "SetX" + }, + "parameters": [ + "Object", + "-", + "Object.Variable(__ShakeObject_DisplacementTravelledX)" + ] + }, + { + "type": { + "value": "SetY" + }, + "parameters": [ + "Object", + "-", + "Object.Variable(__ShakeObject_DisplacementTravelledY)" + ] + } + ] + }, + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [ + { + "type": { + "value": "VarObjet" + }, + "parameters": [ + "Object", + "__ShakeObject_PowerAngle", + "!=", + "0" + ] + } + ], + "actions": [ + { + "type": { + "value": "SetAngle" + }, + "parameters": [ + "Object", + "-", + "Object.Variable(__ShakeObject_AngleTravelled)" + ] + } + ] + }, + { + "type": "BuiltinCommonInstructions::Comment", + "color": { + "b": 109, + "g": 230, + "r": 255, + "textB": 0, + "textG": 0, + "textR": 0 + }, + "comment": "Reset drift detection variables" + }, + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [], + "actions": [ + { + "type": { + "value": "ModVarObjet" + }, + "parameters": [ + "Object", + "__ShakeObject_DisplacementTravelledX", + "=", + "0" + ] + }, + { + "type": { + "value": "ModVarObjet" + }, + "parameters": [ + "Object", + "__ShakeObject_DisplacementTravelledY", + "=", + "0" + ] + }, + { + "type": { + "value": "ModVarObjet" + }, + "parameters": [ + "Object", + "__ShakeObject_AngleTravelled", + "=", + "0" + ] + } + ] + } + ], + "parameters": [] + } + ] + } + ], + "parameters": [] + } + ], + "parameters": [] + } + ], + "parameters": [ + { + "description": "Object", + "name": "Object", + "type": "object" + }, + { + "description": "Behavior", + "name": "Behavior", + "supplementaryInformation": "ShakeObject::ShakeObject_PositionAngle", + "type": "behavior" + } + ], + "objectGroups": [] + }, + { + "description": "Stop any shaking of object that was initiated by the Shake Object extension.", + "fullName": "Stop shaking the object", + "functionType": "Action", + "name": "StopShaking", + "sentence": "Stop shaking _PARAM0_", + "events": [ + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [ + { + "type": { + "value": "ShakeObject::ShakeObject_PositionAngle::IsShaking" + }, + "parameters": [ + "Object", + "Behavior", + "" + ] + } + ], + "actions": [ + { + "type": { + "value": "ModVarObjet" + }, + "parameters": [ + "Object", + "__ShakeObject_ShakeInProgress", + "=", + "-1" + ] + } + ] + } + ], + "parameters": [ + { + "description": "Object", + "name": "Object", + "type": "object" + }, + { + "description": "Behavior", + "name": "Behavior", + "supplementaryInformation": "ShakeObject::ShakeObject_PositionAngle", + "type": "behavior" + } + ], + "objectGroups": [] + }, + { + "description": "Check if an object is shaking.", + "fullName": "Check if an object is shaking", + "functionType": "Condition", + "name": "IsShaking", + "sentence": "_PARAM0_ is shaking", + "events": [ + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [ + { + "type": { + "value": "VarObjet" + }, + "parameters": [ + "Object", + "__ShakeObject_ShakeInProgress", + "=", + "1" + ] + } + ], + "actions": [ + { + "type": { + "value": "SetReturnBoolean" + }, + "parameters": [ + "True" + ] + } + ] + } + ], + "parameters": [ + { + "description": "Object", + "name": "Object", + "type": "object" + }, + { + "description": "Behavior", + "name": "Behavior", + "supplementaryInformation": "ShakeObject::ShakeObject_PositionAngle", + "type": "behavior" + } + ], + "objectGroups": [] + } + ], + "eventsFunctionsFolderStructure": { + "folderName": "__ROOT", + "children": [ + { + "functionName": "ShakeObject_PositionAngle" + }, + { + "functionName": "doStepPostEvents" + }, + { + "functionName": "StopShaking" + }, + { + "functionName": "IsShaking" + } + ] + }, + "propertyDescriptors": [], + "propertiesFolderStructure": { + "folderName": "__ROOT" + } + }, + { + "description": "Shake objects with translation, rotation and scale.", + "fullName": "Shake object (position, angle, scale)", + "helpPath": "", + "iconUrl": "", + "name": "ShakeObject_PositionAngleScale", + "objectType": "", + "previewIconUrl": "", + "eventsFunctions": [ + { + "description": "Shake an object, using one or more ways to shake (position, angle, scale). Make sure to \"Stop shaking\" before starting a new shake if it uses different parameters.", + "fullName": "Shake object (position, angle, scale)", + "functionType": "Action", + "name": "ShakeObject_PositionAngleScale", + "sentence": "Shake object _PARAM0_ for _PARAM2_ seconds. Modify position amplitude _PARAM3_ on X axis and _PARAM4_ on Y axis, angle rotation amplitude _PARAM5_, and scale amplitude _PARAM6_. Wait _PARAM7_ seconds between shakes. Keep shaking until stopped: _PARAM8_", + "events": [ + { + "colorB": 228, + "colorG": 176, + "colorR": 74, + "creationTime": 0, + "name": "Shake Object", + "source": "", + "type": "BuiltinCommonInstructions::Group", + "events": [ + { + "type": "BuiltinCommonInstructions::Comment", + "color": { + "b": 109, + "g": 230, + "r": 255, + "textB": 0, + "textG": 0, + "textR": 0 + }, + "comment": "Start/Reset duration timer" + }, + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [], + "actions": [ + { + "type": { + "value": "ResetObjectTimer" + }, + "parameters": [ + "Object", + "\"__ShakeObject_DurationTimer\"" + ] + } + ] + }, + { + "type": "BuiltinCommonInstructions::Comment", + "color": { + "b": 109, + "g": 230, + "r": 255, + "textB": 0, + "textG": 0, + "textR": 0 + }, + "comment": "Pass input parameters to global variables so that onScenePostEvents can use them" + }, + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [], + "actions": [ + { + "type": { + "value": "ModVarObjet" + }, + "parameters": [ + "Object", + "__ShakeObject_Duration", + "=", + "Duration" + ] + }, + { + "type": { + "value": "ModVarObjet" + }, + "parameters": [ + "Object", + "__ShakeObject_PowerX", + "=", + "PowerX" + ] + }, + { + "type": { + "value": "ModVarObjet" + }, + "parameters": [ + "Object", + "__ShakeObject_PowerY", + "=", + "PowerY" + ] + }, + { + "type": { + "value": "ModVarObjet" + }, + "parameters": [ + "Object", + "__ShakeObject_PowerAngle", + "=", + "PowerAngle" + ] + }, + { + "type": { + "value": "ModVarObjet" + }, + "parameters": [ + "Object", + "__ShakeObject_PowerScale", + "=", + "PowerScale" + ] + }, + { + "type": { + "value": "ModVarObjet" + }, + "parameters": [ + "Object", + "__ShakeObject_TimeBetweenShakes", + "=", + "TimeBetweenShakes" + ] + } + ] + }, + { + "type": "BuiltinCommonInstructions::Comment", + "color": { + "b": 109, + "g": 230, + "r": 255, + "textB": 0, + "textG": 0, + "textR": 0 + }, + "comment": "Determine if the shake should keep going until stopped" + }, + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [], + "actions": [ + { + "type": { + "value": "SetObjectVariableAsBoolean" + }, + "parameters": [ + "Object", + "__ShakeObject_ShakeForever", + "False" + ] + } + ] + }, + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [ + { + "type": { + "value": "BooleanVariable" + }, + "parameters": [ + "ShakeForever", + "True", + "" + ] + } + ], + "actions": [ + { + "type": { + "value": "SetObjectVariableAsBoolean" + }, + "parameters": [ + "Object", + "__ShakeObject_ShakeForever", + "True" + ] + } + ] + }, + { + "type": "BuiltinCommonInstructions::Comment", + "color": { + "b": 109, + "g": 230, + "r": 255, + "textB": 0, + "textG": 0, + "textR": 0 + }, + "comment": "Add default values if none were provided" + }, + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [ + { + "type": { + "value": "VarObjet" + }, + "parameters": [ + "Object", + "__ShakeObject_Duration", + "=", + "0" + ] + } + ], + "actions": [ + { + "type": { + "value": "ModVarObjet" + }, + "parameters": [ + "Object", + "__ShakeObject_Duration", + "=", + "0.5" + ] + } + ] + }, + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [ + { + "type": { + "value": "VarObjet" + }, + "parameters": [ + "Object", + "__ShakeObject_TimeBetweenShakes", + "=", + "0" + ] + } + ], + "actions": [ + { + "type": { + "value": "ModVarObjet" + }, + "parameters": [ + "Object", + "__ShakeObject_TimeBetweenShakes", + "=", + "0.08" + ] + } + ] + }, + { + "type": "BuiltinCommonInstructions::Comment", + "color": { + "b": 109, + "g": 230, + "r": 255, + "textB": 0, + "textG": 0, + "textR": 0 + }, + "comment": "If duration is less than a single shake, increase duration to make 1 full shake" + }, + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [ + { + "type": { + "value": "VarObjet" + }, + "parameters": [ + "Object", + "__ShakeObject_Duration", + "<", + "Object.Variable(__ShakeObject_TimeBetweenShakes)" + ] + } + ], + "actions": [ + { + "type": { + "value": "ModVarObjet" + }, + "parameters": [ + "Object", + "__ShakeObject_Duration", + "=", + "Object.Variable(__ShakeObject_TimeBetweenShakes)" + ] + } + ] + }, + { + "type": "BuiltinCommonInstructions::Comment", + "color": { + "b": 109, + "g": 230, + "r": 255, + "textB": 0, + "textG": 0, + "textR": 0 + }, + "comment": "Detect initial shake" + }, + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [ + { + "type": { + "value": "VarObjet" + }, + "parameters": [ + "Object", + "__ShakeObject_ShakeInProgress", + "=", + "0" + ] + } + ], + "actions": [ + { + "type": { + "value": "ModVarObjet" + }, + "parameters": [ + "Object", + "__ShakeObject_InitialShake", + "=", + "1" + ] + } + ] + }, + { + "type": "BuiltinCommonInstructions::Comment", + "color": { + "b": 109, + "g": 230, + "r": 255, + "textB": 0, + "textG": 0, + "textR": 0 + }, + "comment": "Initiate the onScenePostEvents function" + }, + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [], + "actions": [ + { + "type": { + "value": "ModVarObjet" + }, + "parameters": [ + "Object", + "__ShakeObject_ShakeInProgress", + "=", + "1" + ] + } + ] + } + ], + "parameters": [] + } + ], + "parameters": [ + { + "description": "Object", + "name": "Object", + "type": "object" + }, + { + "description": "Behavior", + "name": "Behavior", + "supplementaryInformation": "ShakeObject::ShakeObject_PositionAngleScale", + "type": "behavior" + }, + { + "description": "Duration of shake (in seconds) (Default: 0.5)", + "name": "Duration", + "type": "expression" + }, + { + "description": "Amplitude of postion shake in X direction (in pixels) (For example: 5)", + "name": "PowerX", + "type": "expression" + }, + { + "description": "Amplitude of position shake in Y direction (in pixels) (For example: 5)", + "longDescription": "Use a negative number to make the single-shake move in the opposite direction.", + "name": "PowerY", + "type": "expression" + }, + { + "description": "Amplitude of angle rotation shake (in degrees) (For example: 5)", + "name": "PowerAngle", + "type": "expression" + }, + { + "description": "Amplitude of scale shake (in percent change) (For example: 5)", + "name": "PowerScale", + "type": "expression" + }, + { + "description": "Amount of time between shakes (in seconds) (Default: 0.08)", + "longDescription": "For a single-shake effect, set it to the same value as \"Duration\".", + "name": "TimeBetweenShakes", + "type": "expression" + }, + { + "description": "Keep shaking until stopped", + "longDescription": "Duration value will be ignored", + "name": "ShakeForever", + "type": "yesorno" + } + ], + "objectGroups": [] + }, + { + "fullName": "", + "functionType": "Action", + "name": "doStepPostEvents", + "sentence": "", + "events": [ + { + "colorB": 228, + "colorG": 176, + "colorR": 74, + "creationTime": 0, + "name": "Shake Object", + "source": "", + "type": "BuiltinCommonInstructions::Group", + "events": [ + { + "colorB": 228, + "colorG": 176, + "colorR": 74, + "creationTime": 0, + "name": "Start shaking", + "source": "", + "type": "BuiltinCommonInstructions::Group", + "events": [ + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [ + { + "type": { + "value": "VarObjet" + }, + "parameters": [ + "Object", + "__ShakeObject_ShakeInProgress", + "=", + "1" + ] + } + ], + "actions": [], + "events": [ + { + "type": "BuiltinCommonInstructions::Comment", + "color": { + "b": 109, + "g": 230, + "r": 255, + "textB": 0, + "textG": 0, + "textR": 0 + }, + "comment": "Keep object shaking forever (if desired)" + }, + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [ + { + "type": { + "value": "ObjectVariableAsBoolean" + }, + "parameters": [ + "Object", + "__ShakeObject_ShakeForever", + "True" + ] + } + ], + "actions": [ + { + "type": { + "value": "ModVarObjet" + }, + "parameters": [ + "Object", + "__ShakeObject_Duration", + "=", + "100" + ] + }, + { + "type": { + "value": "ResetObjectTimer" + }, + "parameters": [ + "Object", + "\"__ShakeObject_DurationTimer\"" + ] + } + ] + }, + { + "colorB": 228, + "colorG": 176, + "colorR": 74, + "creationTime": 0, + "name": "Calculate movement of the shake", + "source": "", + "type": "BuiltinCommonInstructions::Group", + "events": [ + { + "type": "BuiltinCommonInstructions::Comment", + "color": { + "b": 109, + "g": 230, + "r": 255, + "textB": 0, + "textG": 0, + "textR": 0 + }, + "comment": "Run once before every shake movement" + }, + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [ + { + "type": { + "value": "BuiltinCommonInstructions::Or" + }, + "parameters": [], + "subInstructions": [ + { + "type": { + "value": "VarObjet" + }, + "parameters": [ + "Object", + "__ShakeObject_InitialShake", + "=", + "1" + ] + }, + { + "type": { + "value": "ObjectTimer" + }, + "parameters": [ + "Object", + "\"__ShakeObject_ShakeTimer\"", + "Object.Variable(__ShakeObject_TimeBetweenShakes)" + ] + } + ] + } + ], + "actions": [ + { + "type": { + "value": "ResetObjectTimer" + }, + "parameters": [ + "Object", + "\"__ShakeObject_ShakeTimer\"" + ] + }, + { + "type": { + "value": "ModVarObjet" + }, + "parameters": [ + "Object", + "__ShakeObject_ShakeCounter", + "+", + "1" + ] + } + ], + "events": [ + { + "colorB": 228, + "colorG": 176, + "colorR": 74, + "creationTime": 0, + "folded": true, + "name": "Correct for drift and reset drift tracking variables", + "source": "", + "type": "BuiltinCommonInstructions::Group", + "events": [ + { + "type": "BuiltinCommonInstructions::Comment", + "color": { + "b": 109, + "g": 230, + "r": 255, + "textB": 0, + "textG": 0, + "textR": 0 + }, + "comment": "Move to correct any drift from previous shake" + }, + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [ + { + "type": { + "value": "BuiltinCommonInstructions::Or" + }, + "parameters": [], + "subInstructions": [ + { + "type": { + "value": "VarObjet" + }, + "parameters": [ + "Object", + "__ShakeObject_PowerX", + "!=", + "0" + ] + }, + { + "type": { + "value": "VarObjet" + }, + "parameters": [ + "Object", + "__ShakeObject_PowerY", + "!=", + "0" + ] + } + ] + } + ], + "actions": [ + { + "type": { + "value": "SetX" + }, + "parameters": [ + "Object", + "-", + "Object.Variable(__ShakeObject_DisplacementTravelledX)" + ] + }, + { + "type": { + "value": "SetY" + }, + "parameters": [ + "Object", + "-", + "Object.Variable(__ShakeObject_DisplacementTravelledY)" + ] + } + ] + }, + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [ + { + "type": { + "value": "VarObjet" + }, + "parameters": [ + "Object", + "__ShakeObject_PowerAngle", + "!=", + "0" + ] + } + ], + "actions": [ + { + "type": { + "value": "SetAngle" + }, + "parameters": [ + "Object", + "-", + "Object.Variable(__ShakeObject_AngleTravelled)" + ] + } + ] + }, + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [ + { + "type": { + "value": "VarObjet" + }, + "parameters": [ + "Object", + "__ShakeObject_PowerScale", + "!=", + "0" + ] + } + ], + "actions": [ + { + "type": { + "value": "ScalableCapability::ScalableBehavior::SetValue" + }, + "parameters": [ + "Object", + "Scale", + "-", + "Object.Variable(__ShakeObject_ScaleTravelled)" + ] + } + ] + }, + { + "type": "BuiltinCommonInstructions::Comment", + "color": { + "b": 109, + "g": 230, + "r": 255, + "textB": 0, + "textG": 0, + "textR": 0 + }, + "comment": "Reset drift detection variables" + }, + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [], + "actions": [ + { + "type": { + "value": "ModVarObjet" + }, + "parameters": [ + "Object", + "__ShakeObject_DisplacementTravelledX", + "=", + "0" + ] + }, + { + "type": { + "value": "ModVarObjet" + }, + "parameters": [ + "Object", + "__ShakeObject_DisplacementTravelledY", + "=", + "0" + ] + }, + { + "type": { + "value": "ModVarObjet" + }, + "parameters": [ + "Object", + "__ShakeObject_AngleTravelled", + "=", + "0" + ] + }, + { + "type": { + "value": "ModVarObjet" + }, + "parameters": [ + "Object", + "__ShakeObject_ScaleTravelled", + "=", + "0" + ] + } + ] + } + ], + "parameters": [] + }, + { + "type": "BuiltinCommonInstructions::Comment", + "color": { + "b": 109, + "g": 230, + "r": 255, + "textB": 0, + "textG": 0, + "textR": 0 + }, + "comment": "Calculate Position Shake" + }, + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [ + { + "type": { + "value": "BuiltinCommonInstructions::Or" + }, + "parameters": [], + "subInstructions": [ + { + "type": { + "value": "VarObjet" + }, + "parameters": [ + "Object", + "__ShakeObject_PowerX", + "!=", + "0" + ] + }, + { + "type": { + "value": "VarObjet" + }, + "parameters": [ + "Object", + "__ShakeObject_PowerY", + "!=", + "0" + ] + } + ] + } + ], + "actions": [], + "events": [ + { + "type": "BuiltinCommonInstructions::Comment", + "color": { + "b": 109, + "g": 230, + "r": 255, + "textB": 0, + "textG": 0, + "textR": 0 + }, + "comment": "PositionDisplacement = (DesiredDuration - RunningTimer) / DesiredDuration * Amplitude * [-1 or 1]" + }, + { + "type": "BuiltinCommonInstructions::Comment", + "color": { + "b": 109, + "g": 230, + "r": 255, + "textB": 0, + "textG": 0, + "textR": 0 + }, + "comment": "Make initial shake NOT random so users can set a direction for a one-shake effect" + }, + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [ + { + "type": { + "value": "VarObjet" + }, + "parameters": [ + "Object", + "__ShakeObject_InitialShake", + "=", + "1" + ] + } + ], + "actions": [], + "events": [ + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [ + { + "type": { + "value": "VarObjet" + }, + "parameters": [ + "Object", + "__ShakeObject_PowerX", + "!=", + "0" + ] + } + ], + "actions": [ + { + "type": { + "value": "ModVarObjet" + }, + "parameters": [ + "Object", + "__ShakeObject_DisplacementX", + "=", + "(Object.Variable(__ShakeObject_Duration) - Object.ObjectTimerElapsedTime(\"__ShakeObject_DurationTimer\")) / Object.Variable(__ShakeObject_Duration) * Object.Variable(__ShakeObject_PowerX)" + ] + } + ] + }, + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [ + { + "type": { + "value": "VarObjet" + }, + "parameters": [ + "Object", + "__ShakeObject_PowerY", + "!=", + "0" + ] + } + ], + "actions": [ + { + "type": { + "value": "ModVarObjet" + }, + "parameters": [ + "Object", + "__ShakeObject_DisplacementY", + "=", + "(Object.Variable(__ShakeObject_Duration) - Object.ObjectTimerElapsedTime(\"__ShakeObject_DurationTimer\")) / Object.Variable(__ShakeObject_Duration) * Object.Variable(__ShakeObject_PowerY)" + ] + } + ] + } + ] + }, + { + "type": "BuiltinCommonInstructions::Comment", + "color": { + "b": 109, + "g": 230, + "r": 255, + "textB": 0, + "textG": 0, + "textR": 0 + }, + "comment": "After initial shake pick a random direction" + }, + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [ + { + "type": { + "value": "VarObjet" + }, + "parameters": [ + "Object", + "__ShakeObject_InitialShake", + "=", + "0" + ] + } + ], + "actions": [], + "events": [ + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [ + { + "type": { + "value": "VarObjet" + }, + "parameters": [ + "Object", + "__ShakeObject_PowerX", + "!=", + "0" + ] + } + ], + "actions": [ + { + "type": { + "value": "ModVarObjet" + }, + "parameters": [ + "Object", + "__ShakeObject_DisplacementX", + "=", + "(Object.Variable(__ShakeObject_Duration) - Object.ObjectTimerElapsedTime(\"__ShakeObject_DurationTimer\")) / Object.Variable(__ShakeObject_Duration) * Object.Variable(__ShakeObject_PowerX) * RandomWithStep(-1, 1, 2)" + ] + } + ] + }, + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [ + { + "type": { + "value": "VarObjet" + }, + "parameters": [ + "Object", + "__ShakeObject_PowerY", + "!=", + "0" + ] + } + ], + "actions": [ + { + "type": { + "value": "ModVarObjet" + }, + "parameters": [ + "Object", + "__ShakeObject_DisplacementY", + "=", + "(Object.Variable(__ShakeObject_Duration) - Object.ObjectTimerElapsedTime(\"__ShakeObject_DurationTimer\")) / Object.Variable(__ShakeObject_Duration) * Object.Variable(__ShakeObject_PowerY) * RandomWithStep(-1, 1, 2)" + ] + } + ] + } + ] + } + ] + }, + { + "type": "BuiltinCommonInstructions::Comment", + "color": { + "b": 109, + "g": 230, + "r": 255, + "textB": 0, + "textG": 0, + "textR": 0 + }, + "comment": "Calculate Rotation (angle) shake " + }, + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [ + { + "type": { + "value": "VarObjet" + }, + "parameters": [ + "Object", + "__ShakeObject_PowerAngle", + "!=", + "0" + ] + } + ], + "actions": [], + "events": [ + { + "type": "BuiltinCommonInstructions::Comment", + "color": { + "b": 109, + "g": 230, + "r": 255, + "textB": 0, + "textG": 0, + "textR": 0 + }, + "comment": "Every \"even\" shake, rotate clockwise" + }, + { + "type": "BuiltinCommonInstructions::Comment", + "color": { + "b": 109, + "g": 230, + "r": 255, + "textB": 0, + "textG": 0, + "textR": 0 + }, + "comment": "AngleDisplacement = (DesiredDuration - RunningTimer) / DesiredDuration * Amplitude" + }, + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [ + { + "type": { + "value": "BuiltinCommonInstructions::CompareNumbers" + }, + "parameters": [ + "mod(Object.Variable(__ShakeObject_ShakeCounter),2)", + "=", + "0" + ] + } + ], + "actions": [ + { + "type": { + "value": "ModVarObjet" + }, + "parameters": [ + "Object", + "__ShakeObject_DisplacementAngle", + "=", + "-1 * (Object.Variable(__ShakeObject_Duration) - Object.ObjectTimerElapsedTime(\"__ShakeObject_DurationTimer\")) / Object.Variable(__ShakeObject_Duration) * Object.Variable(__ShakeObject_PowerAngle)" + ] + } + ] + }, + { + "type": "BuiltinCommonInstructions::Comment", + "color": { + "b": 109, + "g": 230, + "r": 255, + "textB": 0, + "textG": 0, + "textR": 0 + }, + "comment": "Every \"odd\" shake, rotate counter-clockwise" + }, + { + "type": "BuiltinCommonInstructions::Comment", + "color": { + "b": 109, + "g": 230, + "r": 255, + "textB": 0, + "textG": 0, + "textR": 0 + }, + "comment": "AngleDisplacement = (DesiredDuration - RunningTimer) / DesiredDuration * Amplitude" + }, + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [ + { + "type": { + "value": "BuiltinCommonInstructions::CompareNumbers" + }, + "parameters": [ + "mod(Object.Variable(__ShakeObject_ShakeCounter),2)", + "=", + "1" + ] + } + ], + "actions": [ + { + "type": { + "value": "ModVarObjet" + }, + "parameters": [ + "Object", + "__ShakeObject_DisplacementAngle", + "=", + "(Object.Variable(__ShakeObject_Duration) - Object.ObjectTimerElapsedTime(\"__ShakeObject_DurationTimer\")) / Object.Variable(__ShakeObject_Duration) * Object.Variable(__ShakeObject_PowerAngle)" + ] + } + ] + } + ] + }, + { + "type": "BuiltinCommonInstructions::Comment", + "color": { + "b": 109, + "g": 230, + "r": 255, + "textB": 0, + "textG": 0, + "textR": 0 + }, + "comment": "Calculate Scale shake" + }, + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [ + { + "type": { + "value": "VarObjet" + }, + "parameters": [ + "Object", + "__ShakeObject_PowerScale", + "!=", + "0" + ] + } + ], + "actions": [], + "events": [ + { + "type": "BuiltinCommonInstructions::Comment", + "color": { + "b": 109, + "g": 230, + "r": 255, + "textB": 0, + "textG": 0, + "textR": 0 + }, + "comment": "Calculate object scale displacement, with linear decay over time" + }, + { + "type": "BuiltinCommonInstructions::Comment", + "color": { + "b": 109, + "g": 230, + "r": 255, + "textB": 0, + "textG": 0, + "textR": 0 + }, + "comment": "ScaleDisplacement = (DesiredDuration - RunningTimer) / DesiredDuration * Amplitude * 1/100" + }, + { + "type": "BuiltinCommonInstructions::Comment", + "color": { + "b": 109, + "g": 230, + "r": 255, + "textB": 0, + "textG": 0, + "textR": 0 + }, + "comment": "Every even shake, increase scale" + }, + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [ + { + "type": { + "value": "BuiltinCommonInstructions::CompareNumbers" + }, + "parameters": [ + "mod(Object.Variable(__ShakeObject_ShakeCounter),2)", + "=", + "0" + ] + } + ], + "actions": [ + { + "type": { + "value": "ModVarObjet" + }, + "parameters": [ + "Object", + "__ShakeObject_DisplacementScale", + "=", + "-1 * (Object.Variable(__ShakeObject_Duration) - Object.ObjectTimerElapsedTime(\"__ShakeObject_DurationTimer\")) / Object.Variable(__ShakeObject_Duration) * Object.Variable(__ShakeObject_PowerScale) * (1/100)" + ] + } + ] + }, + { + "type": "BuiltinCommonInstructions::Comment", + "color": { + "b": 109, + "g": 230, + "r": 255, + "textB": 0, + "textG": 0, + "textR": 0 + }, + "comment": "Every \"odd\" shake, decrease scale" + }, + { + "type": "BuiltinCommonInstructions::Comment", + "color": { + "b": 109, + "g": 230, + "r": 255, + "textB": 0, + "textG": 0, + "textR": 0 + }, + "comment": "ScaleDisplacement = -1 * (DesiredDuration - RunningTimer) / DesiredDuration * Amplitude * 1/100" + }, + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [ + { + "type": { + "value": "BuiltinCommonInstructions::CompareNumbers" + }, + "parameters": [ + "mod(Object.Variable(__ShakeObject_ShakeCounter),2)", + "=", + "1" + ] + } + ], + "actions": [ + { + "type": { + "value": "ModVarObjet" + }, + "parameters": [ + "Object", + "__ShakeObject_DisplacementScale", + "=", + "(Object.Variable(__ShakeObject_Duration) - Object.ObjectTimerElapsedTime(\"__ShakeObject_DurationTimer\")) / Object.Variable(__ShakeObject_Duration) * Object.Variable(__ShakeObject_PowerScale) * (1/100)" + ] + } + ] + } + ] + }, + { + "type": "BuiltinCommonInstructions::Comment", + "color": { + "b": 109, + "g": 230, + "r": 255, + "textB": 0, + "textG": 0, + "textR": 0 + }, + "comment": "Save that initial shake has been processed" + }, + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [], + "actions": [ + { + "type": { + "value": "ModVarObjet" + }, + "parameters": [ + "Object", + "__ShakeObject_InitialShake", + "=", + "0" + ] + } + ] + } + ] + } + ], + "parameters": [] + }, + { + "colorB": 228, + "colorG": 176, + "colorR": 74, + "creationTime": 0, + "name": "Move object", + "source": "", + "type": "BuiltinCommonInstructions::Group", + "events": [ + { + "type": "BuiltinCommonInstructions::Comment", + "color": { + "b": 109, + "g": 230, + "r": 255, + "textB": 0, + "textG": 0, + "textR": 0 + }, + "comment": "Calculate the fraction of shake that occured during this frame" + }, + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [], + "actions": [ + { + "type": { + "value": "ModVarObjet" + }, + "parameters": [ + "Object", + "__ShakeObject_PercentTimeElapsedThisFrame", + "=", + "min(1,TimeDelta()/Object.Variable(__ShakeObject_TimeBetweenShakes))" + ] + } + ] + }, + { + "type": "BuiltinCommonInstructions::Comment", + "color": { + "b": 109, + "g": 230, + "r": 255, + "textB": 0, + "textG": 0, + "textR": 0 + }, + "comment": "Increase change for the first half of the shake (move away from original values)" + }, + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [ + { + "type": { + "inverted": true, + "value": "ObjectTimer" + }, + "parameters": [ + "Object", + "\"__ShakeObject_ShakeTimer\"", + "Object.Variable(__ShakeObject_TimeBetweenShakes)/2" + ] + } + ], + "actions": [], + "events": [ + { + "type": "BuiltinCommonInstructions::Comment", + "color": { + "b": 109, + "g": 230, + "r": 255, + "textB": 0, + "textG": 0, + "textR": 0 + }, + "comment": "Change position" + }, + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [ + { + "type": { + "value": "BuiltinCommonInstructions::Or" + }, + "parameters": [], + "subInstructions": [ + { + "type": { + "value": "VarObjet" + }, + "parameters": [ + "Object", + "__ShakeObject_PowerX", + "!=", + "0" + ] + }, + { + "type": { + "value": "VarObjet" + }, + "parameters": [ + "Object", + "__ShakeObject_PowerY", + "!=", + "0" + ] + } + ] + } + ], + "actions": [ + { + "type": { + "value": "SetX" + }, + "parameters": [ + "Object", + "+", + "round(1024 * Object.Variable(__ShakeObject_DisplacementX) * Object.Variable(__ShakeObject_PercentTimeElapsedThisFrame)) / 1024" + ] + }, + { + "type": { + "value": "SetY" + }, + "parameters": [ + "Object", + "+", + "round(1024 * Object.Variable(__ShakeObject_DisplacementY) * Object.Variable(__ShakeObject_PercentTimeElapsedThisFrame)) / 1024" + ] + } + ], + "events": [ + { + "type": "BuiltinCommonInstructions::Comment", + "color": { + "b": 109, + "g": 230, + "r": 255, + "textB": 0, + "textG": 0, + "textR": 0 + }, + "comment": "Save movement to calculate drift" + }, + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [], + "actions": [ + { + "type": { + "value": "ModVarObjet" + }, + "parameters": [ + "Object", + "__ShakeObject_DisplacementTravelledX", + "+", + "round(1024 * Object.Variable(__ShakeObject_DisplacementX) * Object.Variable(__ShakeObject_PercentTimeElapsedThisFrame)) / 1024" + ] + }, + { + "type": { + "value": "ModVarObjet" + }, + "parameters": [ + "Object", + "__ShakeObject_DisplacementTravelledY", + "+", + "round(1024 * Object.Variable(__ShakeObject_DisplacementY) * Object.Variable(__ShakeObject_PercentTimeElapsedThisFrame)) / 1024" + ] + } + ] + } + ] + }, + { + "type": "BuiltinCommonInstructions::Comment", + "color": { + "b": 109, + "g": 230, + "r": 255, + "textB": 0, + "textG": 0, + "textR": 0 + }, + "comment": "Change angle" + }, + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [ + { + "type": { + "value": "BuiltinCommonInstructions::CompareNumbers" + }, + "parameters": [ + "Object.Variable(__ShakeObject_PowerAngle)", + "!=", + "0" + ] + } + ], + "actions": [ + { + "type": { + "value": "SetAngle" + }, + "parameters": [ + "Object", + "+", + "round(1024 * Object.Variable(__ShakeObject_DisplacementAngle) * Object.Variable(__ShakeObject_PercentTimeElapsedThisFrame)) / 1024" + ] + } + ], + "events": [ + { + "type": "BuiltinCommonInstructions::Comment", + "color": { + "b": 109, + "g": 230, + "r": 255, + "textB": 0, + "textG": 0, + "textR": 0 + }, + "comment": "Save movement to calculate drift" + }, + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [], + "actions": [ + { + "type": { + "value": "ModVarObjet" + }, + "parameters": [ + "Object", + "__ShakeObject_AngleTravelled", + "+", + "round(1024 * Object.Variable(__ShakeObject_DisplacementAngle) * Object.Variable(__ShakeObject_PercentTimeElapsedThisFrame)) / 1024" + ] + } + ] + } + ] + }, + { + "type": "BuiltinCommonInstructions::Comment", + "color": { + "b": 109, + "g": 230, + "r": 255, + "textB": 0, + "textG": 0, + "textR": 0 + }, + "comment": "Change scale" + }, + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [ + { + "type": { + "value": "BuiltinCommonInstructions::CompareNumbers" + }, + "parameters": [ + "Object.Variable(__ShakeObject_PowerScale)", + "!=", + "0" + ] + } + ], + "actions": [ + { + "type": { + "value": "ScalableCapability::ScalableBehavior::SetValue" + }, + "parameters": [ + "Object", + "Scale", + "+", + "round(1024 * Object.Variable(__ShakeObject_DisplacementScale) * Object.Variable(__ShakeObject_PercentTimeElapsedThisFrame)) / 1024" + ] + } + ], + "events": [ + { + "type": "BuiltinCommonInstructions::Comment", + "color": { + "b": 109, + "g": 230, + "r": 255, + "textB": 0, + "textG": 0, + "textR": 0 + }, + "comment": "Save movement to calculate drift" + }, + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [], + "actions": [ + { + "type": { + "value": "ModVarObjet" + }, + "parameters": [ + "Object", + "__ShakeObject_ScaleTravelled", + "+", + "round(1024 * Object.Variable(__ShakeObject_DisplacementScale) * Object.Variable(__ShakeObject_PercentTimeElapsedThisFrame)) / 1024" + ] + } + ] + } + ] + } + ] + }, + { + "type": "BuiltinCommonInstructions::Comment", + "color": { + "b": 109, + "g": 230, + "r": 255, + "textB": 0, + "textG": 0, + "textR": 0 + }, + "comment": "Decrease change the second half of the shake (return to original position)" + }, + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [ + { + "type": { + "value": "ObjectTimer" + }, + "parameters": [ + "Object", + "\"__ShakeObject_ShakeTimer\"", + "Object.Variable(__ShakeObject_TimeBetweenShakes)/2" + ] + } + ], + "actions": [], + "events": [ + { + "type": "BuiltinCommonInstructions::Comment", + "color": { + "b": 109, + "g": 230, + "r": 255, + "textB": 0, + "textG": 0, + "textR": 0 + }, + "comment": "Change position" + }, + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [ + { + "type": { + "value": "BuiltinCommonInstructions::Or" + }, + "parameters": [], + "subInstructions": [ + { + "type": { + "value": "VarObjet" + }, + "parameters": [ + "Object", + "__ShakeObject_PowerX", + "!=", + "0" + ] + }, + { + "type": { + "value": "VarObjet" + }, + "parameters": [ + "Object", + "__ShakeObject_PowerY", + "!=", + "0" + ] + } + ] + } + ], + "actions": [ + { + "type": { + "value": "SetX" + }, + "parameters": [ + "Object", + "-", + "round(1024 * Object.Variable(__ShakeObject_DisplacementX) * Object.Variable(__ShakeObject_PercentTimeElapsedThisFrame)) / 1024" + ] + }, + { + "type": { + "value": "SetY" + }, + "parameters": [ + "Object", + "-", + "round(1024 * Object.Variable(__ShakeObject_DisplacementY) * Object.Variable(__ShakeObject_PercentTimeElapsedThisFrame)) / 1024" + ] + } + ], + "events": [ + { + "type": "BuiltinCommonInstructions::Comment", + "color": { + "b": 109, + "g": 230, + "r": 255, + "textB": 0, + "textG": 0, + "textR": 0 + }, + "comment": "Save movement to calculate drift" + }, + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [], + "actions": [ + { + "type": { + "value": "ModVarObjet" + }, + "parameters": [ + "Object", + "__ShakeObject_DisplacementTravelledX", + "-", + "round(1024 * Object.Variable(__ShakeObject_DisplacementX) * Object.Variable(__ShakeObject_PercentTimeElapsedThisFrame)) / 1024" + ] + }, + { + "type": { + "value": "ModVarObjet" + }, + "parameters": [ + "Object", + "__ShakeObject_DisplacementTravelledY", + "-", + "round(1024 * Object.Variable(__ShakeObject_DisplacementY) * Object.Variable(__ShakeObject_PercentTimeElapsedThisFrame)) / 1024" + ] + } + ] + } + ] + }, + { + "type": "BuiltinCommonInstructions::Comment", + "color": { + "b": 109, + "g": 230, + "r": 255, + "textB": 0, + "textG": 0, + "textR": 0 + }, + "comment": "Change angle" + }, + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [ + { + "type": { + "value": "BuiltinCommonInstructions::CompareNumbers" + }, + "parameters": [ + "Object.Variable(__ShakeObject_PowerAngle)", + "!=", + "0" + ] + } + ], + "actions": [ + { + "type": { + "value": "SetAngle" + }, + "parameters": [ + "Object", + "-", + "round(1024 * Object.Variable(__ShakeObject_DisplacementAngle) * Object.Variable(__ShakeObject_PercentTimeElapsedThisFrame)) / 1024" + ] + } + ], + "events": [ + { + "type": "BuiltinCommonInstructions::Comment", + "color": { + "b": 109, + "g": 230, + "r": 255, + "textB": 0, + "textG": 0, + "textR": 0 + }, + "comment": "Save movement to calculate drift" + }, + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [], + "actions": [ + { + "type": { + "value": "ModVarObjet" + }, + "parameters": [ + "Object", + "__ShakeObject_AngleTravelled", + "-", + "round(1024 * Object.Variable(__ShakeObject_DisplacementAngle) * Object.Variable(__ShakeObject_PercentTimeElapsedThisFrame)) / 1024" + ] + } + ] + } + ] + }, + { + "type": "BuiltinCommonInstructions::Comment", + "color": { + "b": 109, + "g": 230, + "r": 255, + "textB": 0, + "textG": 0, + "textR": 0 + }, + "comment": "Change scale" + }, + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [ + { + "type": { + "value": "BuiltinCommonInstructions::CompareNumbers" + }, + "parameters": [ + "Object.Variable(__ShakeObject_PowerScale)", + "!=", + "0" + ] + } + ], + "actions": [ + { + "type": { + "value": "ScalableCapability::ScalableBehavior::SetValue" + }, + "parameters": [ + "Object", + "Scale", + "-", + "round(1024 * Object.Variable(__ShakeObject_DisplacementScale) * Object.Variable(__ShakeObject_PercentTimeElapsedThisFrame)) / 1024" + ] + } + ], + "events": [ + { + "type": "BuiltinCommonInstructions::Comment", + "color": { + "b": 109, + "g": 230, + "r": 255, + "textB": 0, + "textG": 0, + "textR": 0 + }, + "comment": "Save movement to calculate drift" + }, + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [], + "actions": [ + { + "type": { + "value": "ModVarObjet" + }, + "parameters": [ + "Object", + "__ShakeObject_ScaleTravelled", + "-", + "round(1024 * Object.Variable(__ShakeObject_DisplacementScale) * Object.Variable(__ShakeObject_PercentTimeElapsedThisFrame)) / 1024" + ] + } + ] + } + ] + } + ] + } + ], + "parameters": [] + } + ] + } + ], + "parameters": [] + }, + { + "colorB": 228, + "colorG": 176, + "colorR": 74, + "creationTime": 0, + "name": "Stop shaking", + "source": "", + "type": "BuiltinCommonInstructions::Group", + "events": [ + { + "type": "BuiltinCommonInstructions::Comment", + "color": { + "b": 109, + "g": 230, + "r": 255, + "textB": 0, + "textG": 0, + "textR": 0 + }, + "comment": "Stop shaking when the duration has been reached (or if the stop shaking function was called)" + }, + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [ + { + "type": { + "value": "BuiltinCommonInstructions::Or" + }, + "parameters": [], + "subInstructions": [ + { + "type": { + "value": "ObjectTimer" + }, + "parameters": [ + "Object", + "\"__ShakeObject_DurationTimer\"", + "Object.Variable(__ShakeObject_Duration)" + ] + }, + { + "type": { + "value": "VarObjet" + }, + "parameters": [ + "Object", + "__ShakeObject_ShakeInProgress", + "=", + "-1" + ] + } + ] + }, + { + "type": { + "value": "BuiltinCommonInstructions::Once" + }, + "parameters": [] + } + ], + "actions": [ + { + "type": { + "value": "ModVarObjet" + }, + "parameters": [ + "Object", + "__ShakeObject_ShakeInProgress", + "=", + "0" + ] + }, + { + "type": { + "value": "ModVarObjet" + }, + "parameters": [ + "Object", + "__ShakeObject_ShakeCounter", + "=", + "0" + ] + } + ], + "events": [ + { + "colorB": 228, + "colorG": 176, + "colorR": 74, + "creationTime": 0, + "name": "Correct for drift and reset drift tracking variables", + "source": "", + "type": "BuiltinCommonInstructions::Group", + "events": [ + { + "type": "BuiltinCommonInstructions::Comment", + "color": { + "b": 109, + "g": 230, + "r": 255, + "textB": 0, + "textG": 0, + "textR": 0 + }, + "comment": "Move to correct any drift from previous shake" + }, + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [ + { + "type": { + "value": "BuiltinCommonInstructions::Or" + }, + "parameters": [], + "subInstructions": [ + { + "type": { + "value": "VarObjet" + }, + "parameters": [ + "Object", + "__ShakeObject_PowerX", + "!=", + "0" + ] + }, + { + "type": { + "value": "VarObjet" + }, + "parameters": [ + "Object", + "__ShakeObject_PowerY", + "!=", + "0" + ] + } + ] + } + ], + "actions": [ + { + "type": { + "value": "SetX" + }, + "parameters": [ + "Object", + "-", + "Object.Variable(__ShakeObject_DisplacementTravelledX)" + ] + }, + { + "type": { + "value": "SetY" + }, + "parameters": [ + "Object", + "-", + "Object.Variable(__ShakeObject_DisplacementTravelledY)" + ] + } + ] + }, + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [ + { + "type": { + "value": "VarObjet" + }, + "parameters": [ + "Object", + "__ShakeObject_PowerAngle", + "!=", + "0" + ] + } + ], + "actions": [ + { + "type": { + "value": "SetAngle" + }, + "parameters": [ + "Object", + "-", + "Object.Variable(__ShakeObject_AngleTravelled)" + ] + } + ] + }, + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [ + { + "type": { + "value": "VarObjet" + }, + "parameters": [ + "Object", + "__ShakeObject_PowerScale", + "!=", + "0" + ] + } + ], + "actions": [ + { + "type": { + "value": "ScalableCapability::ScalableBehavior::SetValue" + }, + "parameters": [ + "Object", + "Scale", + "-", + "Object.Variable(__ShakeObject_ScaleTravelled)" + ] + } + ] + }, + { + "type": "BuiltinCommonInstructions::Comment", + "color": { + "b": 109, + "g": 230, + "r": 255, + "textB": 0, + "textG": 0, + "textR": 0 + }, + "comment": "Reset drift detection variables" + }, + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [], + "actions": [ + { + "type": { + "value": "ModVarObjet" + }, + "parameters": [ + "Object", + "__ShakeObject_DisplacementTravelledX", + "=", + "0" + ] + }, + { + "type": { + "value": "ModVarObjet" + }, + "parameters": [ + "Object", + "__ShakeObject_DisplacementTravelledY", + "=", + "0" + ] + }, + { + "type": { + "value": "ModVarObjet" + }, + "parameters": [ + "Object", + "__ShakeObject_AngleTravelled", + "=", + "0" + ] + }, + { + "type": { + "value": "ModVarObjet" + }, + "parameters": [ + "Object", + "__ShakeObject_ScaleTravelled", + "=", + "0" + ] + } + ] + } + ], + "parameters": [] + } + ] + } + ], + "parameters": [] + } + ], + "parameters": [] + } + ], + "parameters": [ + { + "description": "Object", + "name": "Object", + "type": "object" + }, + { + "description": "Behavior", + "name": "Behavior", + "supplementaryInformation": "ShakeObject::ShakeObject_PositionAngleScale", + "type": "behavior" + } + ], + "objectGroups": [] + }, + { + "description": "Stop shaking an object.", + "fullName": "Stop shaking an object", + "functionType": "Action", + "name": "StopShaking", + "sentence": "Stop shaking _PARAM0_", + "events": [ + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [ + { + "type": { + "value": "ShakeObject::ShakeObject_PositionAngleScale::IsShaking" + }, + "parameters": [ + "Object", + "Behavior", + "" + ] + } + ], + "actions": [ + { + "type": { + "value": "ModVarObjet" + }, + "parameters": [ + "Object", + "__ShakeObject_ShakeInProgress", + "=", + "-1" + ] + } + ] + } + ], + "parameters": [ + { + "description": "Object", + "name": "Object", + "type": "object" + }, + { + "description": "Behavior", + "name": "Behavior", + "supplementaryInformation": "ShakeObject::ShakeObject_PositionAngleScale", + "type": "behavior" + } + ], + "objectGroups": [] + }, + { + "description": "Check if an object is shaking.", + "fullName": "Check if an object is shaking", + "functionType": "Condition", + "name": "IsShaking", + "sentence": "_PARAM0_ is shaking", + "events": [ + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [ + { + "type": { + "value": "VarObjet" + }, + "parameters": [ + "Object", + "__ShakeObject_ShakeInProgress", + "=", + "1" + ] + } + ], + "actions": [ + { + "type": { + "value": "SetReturnBoolean" + }, + "parameters": [ + "True" + ] + } + ] + } + ], + "parameters": [ + { + "description": "Object", + "name": "Object", + "type": "object" + }, + { + "description": "Behavior", + "name": "Behavior", + "supplementaryInformation": "ShakeObject::ShakeObject_PositionAngleScale", + "type": "behavior" + } + ], + "objectGroups": [] + } + ], + "eventsFunctionsFolderStructure": { + "folderName": "__ROOT", + "children": [ + { + "functionName": "ShakeObject_PositionAngleScale" + }, + { + "functionName": "doStepPostEvents" + }, + { + "functionName": "StopShaking" + }, + { + "functionName": "IsShaking" + } + ] + }, + "propertyDescriptors": [ + { + "value": "", + "type": "Behavior", + "label": "Scalable capability", + "extraInformation": [ + "ScalableCapability::ScalableBehavior" + ], + "choices": [], + "name": "Scale" + } + ], + "propertiesFolderStructure": { + "folderName": "__ROOT", + "children": [ + { + "propertyName": "Scale" + } + ] + } + } + ], + "eventsBasedObjects": [] } ], "externalLayouts": [] diff --git a/examples/starting-tile-placement/assets/StartingButtonTower.png b/examples/starting-tile-placement/assets/StartingButtonTower.png index d9cbab7940c5515f6041dca4e38d4bcb4dabe91f..973ad547e2396fbf4c4196deb3d1626c3831da32 100644 GIT binary patch delta 1378 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 A6aWAK diff --git a/examples/starting-tile-placement/assets/StartingButtonTree.png b/examples/starting-tile-placement/assets/StartingButtonTree.png index 238524c39a8e1aefcb789716f7b6dad4b5bf8800..d0a8d5d981c7de917239e7cb768976782b5578fe 100644 GIT binary patch delta 1587 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 DFmi=p diff --git a/examples/starting-tile-placement/assets/StartingButtonUnit.png b/examples/starting-tile-placement/assets/StartingButtonUnit.png index 5eb6c57d96a22c0e1ad68cf659700d338cec6834..d000b84ce7764c2388b68e5a6623187a75d6b239 100644 GIT binary patch delta 2292 zcmVttt40Do)Tk~JWgW@~-?~&ujOS0{(@CggObbsxN{=iNIyTR1nnS?v28A($_ z9Q0c2>wOh|I2B+b?_o0EpYLP^Hz}^g=bd2G-7rAe4|s>h+c>(nfrlqrngG%QI25gQ zBraB6oW7F7>8p9zg7Oot?YJcueZoZE$4|eoii1RemN^E;VTD2_(ByFY?gKUa{K6-#KLJOn`kblPK^-QD*5wROY1jW`s{Y8omKiF^2 z6|55Qtuif83A!R2qvqr$=kdJ+19j*PY`ESueas-mZ zbS)<8dt|x^8?Z*e6q$Zx(umDJq4ik%iCG~rKI)6&!@e;)$&$4^fTuDRK(S^?zs|kW z5P#_m{PHqXE%IrxsUjh>S;+BmJ+^cClC}(Hh6AY$SR)|zYx+hrJLTs8Ub&ux%xNDVj&iuAxSvr z;9*>S_D^x2nHPYu!=H+UkH)4R*oV%4UVp<<-GL2QBVb|0HGZ7UY=^94NnSQ+gZGQqC z)@pd`nHN#~;sH1gMgN7@Al9q+A~QU)Y6%@(Q(asCxJX{G78R(J86{|&(wtS^Uu^^YPR+S5iZPo_>14G+_FHf%CH zBYsGNE(<6(6xPqTRYZEjFt@ zof1ovF}{7>!K?pFQHhRRh9-uIjEC8xomAMG3;g8X3ex)OUGshO4F~0Ax_{R;8enWZ z0404?l?pTy^_oCsMUwCWUPoyE*HdNa4qw^Tz+5ehM$lWGSs`G5p^d3%4X-xN_kIVs zbwl755n;G-lW%L2WUZUeb!Q3gIy}VJhysz=wk1FTs<^3IyLiqCS`vu;83BM?PS~2JBdXO1gj`Ayl~d*&ej^19J^qu;v1+ zx#(vHWDTynef@RXLMxw}zLo_O=t~e}a5}u5Sisq!xltVzOvK>0fPYd(>)l_h=(e=L z(D3^gfyW1W6{zlR8hH=cvw#hWAPLjv72f+xkXkKX55NeX9Ikp#guNJ`Eg;lh7{%*# zp%*yvUFw@mT!Y4G3))aCNSyZ&)?C1k{TE$Ll5mxZ#OTrV)WL7_`alhs1%xh; zsQT&Gt#zS=EEG1AP=BjRtk;bghLO8Dr|j4?UYfwZJ5t7UP%^Rs8d$D3FAYov||#f(!(mm@`ypKeR-aLqOv68}zmw$TuPy_EF_}kLm-xn0lMiN~F zozP|KakJcdphgG0_CCeN|CG~HdPsCk3N2a zYL6!)B6bWE#W^9LKl9RWpGx@fcX?RW>@$~gmko%luo O0000EX>4Tx04R}tkv&MmKp2MKrb<;R4t5ZA z$j~}j5EXHhDi*;)X)CnqU~=gnG-*guTpR`0f`dPcRRt?f%Ws^E4huXp zWMoqF#35p_*uioKvy!0_PZNg~Rik_%>$1Xmi?dp(vDQ8L3xj!WCCznOqljY(2_zvx zMh#_DU?EDoMt_Ql6rIOB{KJkvNiLaOWiWCqpb8a|;|KqP-@P>pQq)e_ z00006VoOIv00000008+zyMF)x010qNS#tmYE+YT{E+YYWr9XB6000McNliru=?MuH z7Z(vat=j+q02y>eSad^gZEa<4bO1wgWnpw>WFU8GbZ8()Nlj2!fese{00xLjL_t(| z+U=W7Y=2x=6@b5Y-<$V0Gqy96G>KANH#n3)QY02p6cv@i!V!fuWKmeq1xuu$s)Rk= zbwgA|bWs+pip0(kq$shF5Ji!Nkg~7GgV#q_>Sm1KYMY|LYm+{t2(w>k);KmH|XjM4Tj~ zS&EdBJkJ3z3o&jslgIoG z=nwitaYVDxz!c_q0_gU-L~+c1A71-fk7F~P2Y$}6BnPHK4orm{o9Xb~<8@xWJi)a) zDI1**yC$c!m4wjc{2&NM&j0LN>mTa;gMZ}Hd;0wJ+jltgRECt2PPeNqNLm17S;in3 z6uXTx9cJ2tht}cS89(^SJ&v|>k~AR*!|?&o9~2J1`dq}}egFNE+N#I==wuQ7APh#U zMx_CyX-b?FsekI}^=<3>-2NWlIZ`Z}Q5;ntK%B$?eD8RKTg#N_eBld&VHSjA0DmAZ z0DN&eP^R;+KQ)4=gm7li3H2El%EMS z04N#`Z~nm{Ow>f{;*t-5>pJ660)KYRW_19+xjwmVeam<2{QS=rLI@mt3;+Q5UXlK< z{m!KuTHC07gEy}>0q|T;%aK?MfHDi#WBA3jCToL-H?uYnT>6X8rDcH-LR)u8TSG*M z!s&hje_XNHKOvdfC7JZ(fBfD?Ao$xo!D>g)i^Xs!IA$FoDJ5Z499*V%6@MqV)lT7* zW996r=QNxgYnT|RgkEHDw{H-o#+IHyO+b<+NGV59D-WPNwY_pY;kiR0OaHKl(&AZ^ z8oayl(Y5SYaBMo{UN7eSqD>Hn)P2ADbAXhRAS@0-r=IT6@+Dt77;~^CF+au}W*Ts? zReY!AOTKaR{f5Ygb(JfX0e@xm&z*?bI}r^JM(~-(Ql4xVliR=gf*>vG9bnRvcowwl zl9n&2Z7TW4ni1zt=*t470P;K^)&AV!{s*4dOxW9$>}@_=B%eRh<@{osAPn$5wacN> z5{ToNJkNRYaK_`i)b#`QO+>tSINQ>2sU(1K*t2{7^MUeOEp>*9`+tH$W%?&c0>F_2 zedPd3ok@}?ZHp-bP&)t8(G;(iDF@)yGG02GZn0G@0BJF(<1;b3q|{y7suX~D7?mg5 z`T&%=Qjd@Op$8?V2Yemht#&ml>jL30%lqg z-BQ{}s{|m=i$Tq_Q-A#chBiu@)yROfh{o=QegLJ7(q?5QL4bB$KY-Fkr6$32xGsBk zrX4_OBUQ8)Py#>>qcP8 zetXl`4xqGA+N=nG;@0;3qRsW&4Q+_Ae6P;g z`3V4g&#zVq6oc|`?BaI;r{9|7;!;b=4x-Hq$+zR<_*P$N^R@Q9ZzGX9iBS02g-fVeo=^YkB#vv$M0ifnz(&`8TdCEWB<4+*)3~JUctP1ndJI oe}wx4DIf5A;FT*23$FwG8=!oqkye&yivR!s07*qoM6N<$g6h;P!2kdN diff --git a/examples/starting-tile-placement/assets/StartingGroundTilemap.png b/examples/starting-tile-placement/assets/StartingGroundTilemap.png index 3c87bb21268ae12016b70c278ac448e534c619f9..d25eafc90673351ff18572546c38f6ac7e1b8481 100644 GIT binary patch literal 1847 zcmcgsX;c%|623%e5Tt-WL|Vg&0tyBoK_eB2su5 z)<7VlG(kal&s5nY1Y@9;NGt+MvtZe@tYwjq1d@i+zy0lU_5uE{mq1l$}fGUQi zwg|n54i1FQKrKGCH9dQrcUODy8Twd2#FgLYrK#y*(KdZcW8S*7nTut`U$PoP;88t^ z(3&P&f?iGRwGK$U-i(0*hnjytA9FkM!slH#Os3pCq>oE@3Xvhy!#X0(YP<26=)hgO zKA%fm`CbW$Kw#rSQq*maLPyL6(%60Bu{2BO5cj?z*1aH=9rOcx7OwN{Me^ok1B&gX}IZR1U@(T3@<-s zdYJ@ab&W)a5m?!lE4{B%vggY$UKN=>68~iJ;0a~^?L59PyMYSW zkT0Z3QTca1W2#V;F=|mgVbP@XhnR-A9U^9Ffxy8%7;tv;`epYR4yfNQ4HQk0UkPon zdx|nHlTn9SOd)xPIYe_i)$p&u@Ep1?ExO|ip>>jbqdi(B^wJTIG!HiIB^LT(*&S3r zqk$^q8`TP^i2j3-1CmevHKRHoEnisQD6}c>*Ms@!2>+Y#%fTnDEzycBt4|kGcX`eV z9dw)~0*2Hf1WwN(Axx%UJ+S>^sw(7~;3UA@>*jnoT6WGhNAZ}YE;~!-eHwG(_yh@^KDg>4Oh+`|!jXG5tFuj$S<~v)JxmcaeuIt}tLGn-M+JqJ;%n^7 zaErCWtBR#-(J*3WsN~Plm4VqAyB3NJCAP{s*;ukMfoXXcrZ3cUlneN`cS$F?EA()* zy`zWddWvpN;H|Aj)sX*VAGCd?pG}tEj=D2QSe0&$VAL?tt?Z<8cFAdfpL=OCXBnz? z61Njf=bc*&T<#k~wXO%9)u1VMI`JcANauwW&+Z@9aP6l3UDOB9QXo8R0j;rHxv`$; zH5=JShE+GRcV8z_3GM~HHiwkY*;orR+_I!WDB_rnl&Ly-l*B+kBHo9qbT00f708vDJ+SR@M zhK@PAlAwfY6({-W)~_0T+h}9B`18xb2vMj>+Ey>eI6iWbL1M2w*g!uh*SU8IiY{`GBi ze>m|S%9RqsZ+~g&mpU%Bxn1!~DUr}xk(+SEi>zXnloA)Vt@Mc}3dYFJoG%Zp$|4!7 zn!Yn+;%vkq(mmJyW!sgBwf?CiW0~w zJGS|0M7+1CaI?-c1FqDfyc%E2E@R+?)5YrO16z;-00ewAa2Nm`pbJdE#{~cOx~ONM z3cIbG7S6984Nrz(+SM;So;WdFEuxOU3-IU;mo!z6nz6R^KNHrL7W{`*QQvdF<*O7| z=ct{9JqYf8G*_z!qlVTolM)mO;>0i4j2DVU&9k;zW!Oc`bhy{u^(eHszM-b{p~2~^ yaw57DyBl`+)embCNXDGG=Pb>fn0c#yPy^P}>Mk5&n-yzW1JHpX$C{7CQ~w6AS|^$S literal 1758 zcmbVNdoJ#q>^@*X@BkGNR2jOd(8^B{6SHGwF`JX68|1l|*$B^5`mcQjyUBAeZTAd)SRQ^o7q(`y)zCem3gl ztGdft?Nftx6|?Bb&D)=m=K8dfEo9aA5K?W6Hy7*`ZmL+X*uH0r*7JFH$`x1*c!zUP zAr2OxL>+02Z=N>!X4>cY40N*%3+{60!rjec-HNKNf$h``9XHn zkda1B@NnA@@5Q){L>1902z^ntvyr3KfK*c`x~wBdvgU>yaLh;EJps@{%Z9&vs=mqu zGb!A*cBO1w#2uxRu1R+8==)@C5B0O=s^%8vBdeU$46)i;`N<8w2C(@l#=+WQ)rrQL zwhx>N~Bqu++Hy38}2>yvl__dGIA%R}gv6lJL}>qXqh z{+cD4%$gn^?-nIx-6najUkH?)B(hcP<4Bc5TQrfUn#yJacst)KLd(UU z40B3BxQoe_uq7%TclBlAx%%N(W9-wd8S&Gca0`o(-p5pT%uxnV<%8Q6gV<`NIeYF# z$~SkfqtyT#+>lFX_xnI5@r%tAz1K5Kok|1({PB{P4m(D>@@oa02OZ)zPJmr@kki=3 zdEzaJMv<3B#s*k>XNG5#N`ZW`9y7oBa z%B*@aAeAZGhLAXDFp0w0$x4faa-gPOj5uuWE8yLjS(G9YNM3k|NiNITaGuD=LJ^%Y za$zesEfXLHC%~#a<2PkR;$=oh!z+1l!8-3`^Br~Q%6l2|V1JaZ^|I?c zMf|%YI?jg64tW+VOBKrQg1$E#di$oTw`K{NGaa)hol(2CBIvH(`6&(_9K;P`=LMSE zFX@EUV=QSl>-s+fmGquxwA!T^D9bII!0tD5dZ3eaT!fls`>2u$g70urlXT2kUx(oU zfxg8i`{}Y=t$uzxi}r$<0XMkLebPeAF7XS#n*L0wLI~MsQ1|$seUdA#N9R&<=*_Z0Lw*G74? z+_b4%4>eF^j5&S%wpuN~Nie?W#sn${7a8HVl6U&ERIL9#h<{S)wi;b^W>+U%eMqDl zPiAVitjS6tE@^SGc{Y@{t@-n^W{O?|!GL~lKYS?10oRS(_Ub6(pfFVMWTM}RSQYuM* z$7oYZ@x3yZ0Z8fl15n?YL_I*^Cs88259$k6NHPJ4;Gb;y@927ppwBcPGztbZWRgJt z4=rB;%uRdB05h4SPN4XwVRSITS=NlOJGg@OuN(d)Yi?+0I$dRwLyd#)#B`eD3oH>T zMP_){;49Ep+sUU=`WTziic_ZKA+~BuSErE+T_qj4{^2XDR3q%Yirln-;S>{`a@Kdr z>$%UsQxyEz<;sBLW?SeOYd!5RZwYPU04EQaJ8Eb~kdP?T$NViB@5&rpdsu*@or^8~ Ii2tR(00QzRqyPW_ diff --git a/examples/starting-tile-placement/assets/StartingTower.png b/examples/starting-tile-placement/assets/StartingTower.png index 8c374ccb2f33bf66556ac2ba2cf57a215447731e..0f23cc8391260874ba94fede5b30fe021a589b4d 100644 GIT binary patch delta 949 zcmV;m14{hv4ax_QBYyx1a7bBm000XU000XU0RWnu7ytkO0drDELIAGL9O(c600d`2 zO+f$vv5yPcj9=wQmL)s@$pP)5*7k}S`vx$j%@nlkuM2JgB zjJCV;Ia^XgVcTwZzo~@I?>M_No%!$g&HQJkfWzT%I2;a#V=^IRgx^-4>Iear#2d|)j@GAZK{M%TSg&l5!n>aO#M|E1{xf7mAtPq+FHa6c zGphaGZ`bG7w0}Nbrx)^zC^Y&%$HuApOq;EGWfK|6EI`~@*nlvH5h0L|jN}mDd{BTe zAD=966rL{s_EiTfccP*7fB&Mj504i(>Dj^05Ty!367dKqm33ly`6`!?g6SE?g{kCc zmu4W2xB!dGk?CmfS6W!qs9e)&o50OmVUGx&iPNQ($bXb2e|=LLAS2=_5O?PEMGaH5?J@QU*Xqym*JS@QyL?oVC;sU6F2Ojxg3vG)Fz|58=z)tHv z*Gr-zhzfAjIeQV%p$O4=KvaM>;KMRe+j{^(M1KV!6zqO|g9iB!L^cJWE$06b(#x!H z1dtEQNP7WU0W5lef4?E@1!M(a@?jYko@j-A0Q;~{Nzx052{0B9&_&P-hzT$b570%@ z3y2Az;Q1I6l3qaUSX`U@_eMBg&}DO!Sw7OA4jF29__bLV$>ml|fKfUX-YY`i@OsMB zu75(|i@8(`hu;N|;AAV70-i_r)4UfC{N&&}<=4&GuJ5I30!qpG4Y^f-(_K&L<^F?= z{B!{+=>-IRK)SHhvjcvzXj++0HIImS0YL%A+}d4Y-lhlmpZ6OI!A6ONbw=1y=2 zV}qOYJopX)#H_H|+FOq=NzLVjKj=L@#4hQYi%;-?m(00iV2t!O91e%W;cz${j$Hf& X%dy~O+Ms}q00000NkvXXu0mjf`>nFD delta 1759 zcmV<51|a#$2ks4!BYy#fX+uL$Nkc;*aB^>EX>4Tx04R}tkv&MmKp2MKrb<;R4t5ZA z$j~}j5EXHhDi*;)X)CnqU~=gnG-*guTpR`0f`dPcRRt?f%Ws^E4huXp zWMoqF#35p_*uioKvy!0_PZNg~Rik_%>$1Xmi?dp(vDQ8L3xj!WCCznOqljY(2_zvx zMh#_DU?EDoMt_Ql6rIOB{KJkvNiLaOWiWCqpb8a|;|KqP-@P>pQq)e_ z00006VoOIv0RI600RN!9r;`8x010qNS#tmYE+YT{E+YYWr9XB6000McNliru=?MuH z1_mfot~mez02y>eSad^gZEa<4bO1wgWnpw>WFU8GbZ8()Nlj2!fese{00hNJL_t(| z+U?t2h<_Dj9LMp`?5?f4Z|16d+_v46Ox>evwi_xX36exFnAi(;W!OaodX;x+SY8Ac zT@=w9B!RtP7e()xP*F&f8ihn}hLLP(_U39>w$sI#T}C_Wx<_Z`MCS+R;>^tR{Ql2( zp8w2$o->DvDypcWivGU}&Y*1%E=K)H+xxH^JAY5nb_(Vp7k&Q$AL83nwCTp&A^bkU z$7w+uhaCuq`MiMIBJJ0szsT|Z(Y}*a8}&7u-;OA@#Am=$Sb5cSuIXaJN$2CIJ|2CC zJqN)?tc&fw7B9nwtGTG9u<^?ux%;)jW3Iz1u{QVOuKYx9UsT-h)lYfj3$PitCJ7+G zAAhL*>Yl))hT;e9J4DyUP&B2d#&$h}61;H&I>0wYsXXFVdY+o8@&ig}#zf zO;R0CYhm}gz-Qlw{ya`q7472#ovlN+S&|RzSj%@mhx+?Dr-s>WftE)9Jpjp#tc?0F|71)(_A+#%+ za>rI}VduW|2ETJ3tF{aderc(LKO2?cn^*(aJsjGzKl#D$*Ib%Q$Kph5E01hH?#OZQP~=?}mEhA@-TNB{`l=EI(EHm7p4n7_MU;c~=6~cbgfWj? zIXw&Xz~brsmQ4RMQ2>2?q33oEdcIOF0J3EM=@jr;PBzYov2+ln^D#20T@-y zve}?EiXpQUSO}M0D0Rg=7iI}y4oquFe?=(fIVVd1GdT6{c1bDbIU{ob9Am*73 zfFKBR$mJ)}T%SIG=6dpl+hHz>C0zhv7%oh^1Rkm+9*8CF6qpJ#Cx2%LAm%+a3jnjA zX>5=gfSC8JECBSt1lkaHcU-`Jbx- zpaP%*pd=lHuMcEDfLK@z2TOXaFVS}?UWB!asjtmu@O{VFw0-dF^>`^w|8@Aw@c-mU z`tz3)|CEu+3>Z)yF3mU#K2mr?w4#bCs;Huo>L048ESmolmJI*^002ovPDHLkV1n5G BLB{|9 diff --git a/examples/starting-tile-placement/assets/StartingTree.png b/examples/starting-tile-placement/assets/StartingTree.png index ecbdbf4ce0002b85bc124862d65b9d3099243ba6..dad42009f6064b3020f6dfe70cd1de0a76fdaef8 100644 GIT binary patch delta 1207 zcmV;o1W5ay5XuRVBYyx1a7bBm000XU000XU0RWnu7ytkO0drDELIAGL9O(c600d`2 zO+f$vv5yP$dkCaZa% zPVwc?rwxdRNCd!}Pn*@x{Ofc43QGHI|7KJoP>2A_td1SrJi?&g0Am^=LVE;E6mI4C z4X;Dng^TQ+|i^*C%#uH4ImX0!@m^w5P)$0>2< zFhPPwv+>VU5@4}Yjt|3T}8=_!lKBfzcfS?O~v z+i77N(Nz|e3NX2H8_U{g^FADXGu78}ULu*|FH`)uJyp1CgJP5lKna->_gOBZjd(;s z$FeB{WdgXBJ-5+s;1LBm9z{yaq7nfx@$u~1HiwhF>5iW>&317prPC_SkrDywiI3z! z2D~h)G=E3Z%b?e4uL0%&oC+e0IZ)o-1c$%7u@C##RrCycKr)ok@lLXqcQzr=-*qTP zf%HgFVuYqv?#?s9#y}p6&xYPV1O+G&;7Q@mdUFm)BvP(n?*&eWN@eGGhr;!N^eMp0 zUrPeS%-)y12!7cu|AFnTU-b=4gsCcef)S96)PE{~4G-?&XPWm}nP3$R7fa1iLm0`c zXqZS7;2`@84G$cB!$?3-Qo>4+QfC3b2P7g*0Jqv7bo3jbYow&<66+mgsrFck0Cn5j zy66cz`Uv4SZ;JhbgWjP$dmLH-Pupu+AOp9x1KML*Qz5hf^^hbvalhE$2uiiN95#?R z6@NncfGM7p8F7}pj=QYxe8C(C_V%it$GL4Dc`^KP8A1>$0%nKbF7xvA%Ii2rL6}#k zo98U!5i48gYmhYoDCd0#rD=l2Unfk5C# z>%IkWb6|d&9WuZuphBey`e@zPl^-s8Y;pel~1%^e)zVYd3h9U4wx9 z{hqW>I)^4%DRZgz1p+__<5R$Sf$! zi3NZ&PAmYNabf{L7bcV#V;d)(#xR}PSI~&o{}{3Dd_NbWzly1W4~F)=Zk_zxpW VYhMZMI-&po002ovPDHLkV1kFkECB!j delta 2066 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 FV1m?tzzP5W diff --git a/examples/starting-tile-placement/assets/starter_switch.wav b/examples/starting-tile-placement/assets/starter_switch.wav new file mode 100644 index 0000000000000000000000000000000000000000..50df257800a3c238b6be7b0d150a2629bc86ed7e GIT binary patch literal 68288 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 literal 0 HcmV?d00001 diff --git a/examples/starting-tile-placement/assets/starting_woodthump.wav b/examples/starting-tile-placement/assets/starting_woodthump.wav new file mode 100644 index 0000000000000000000000000000000000000000..5949eb5a8c6f3abf7f87d9f10ed3a83be9ee84b9 GIT binary patch literal 35904 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≺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 delta 928 zcmV;R17G~79jgbB8Gi!+006uZmmdHC02@$DR7Ff_aWyqHHa0djH8nOiHZ3hJHa0dm zI5|R^?&H|1(F!EEFDZlRnwi_ z{vfi zj=HzjIAJ?^*aIR1yr3o5f0`Hd%5^<6F6Up zP~Fjl;*KVq#|bI9VKHMl8{r`)iPZ%{-xG$jf4=?q~^F0)IA24NSmi2{R2agHyOAVdeoQ zxPbQ(79!wkIWZ#NI!%xsUi&yP0Snv1t6i5;0fp`1zdpu#*d3k2|9q@mKxK#E_j(Kt z!LJ|`GN7)T^niore1Z;j$NmwypId1iKp=wpMU(OX1$5-Pn-+2DQ3 z3U8HfoorJ0VFQ$enSS6!7WRpkSLewMoFS+@5$ws+?STP?b}+=r0-O&JIbb}yTqpDq z_kD-lXD%rF#snxfL8d2)U`M4yN4i*I^UWcQjl?nfK%C*3a$s;&f%6R|JUAMx!11?+{;EmT?0T5AdBGkQj7xl&{aQ(}c)V~^X*e%3^Jziw;PzI z-Q)Pxrd*^ diff --git a/examples/starting-top-down/assets/Flat dark joystick thumb.png b/examples/starting-top-down/assets/Flat dark joystick thumb.png index acd005cc067032996264e510b58c8dbc0c102b62..8acf179f8660be36ccea1198105da35c07154fd7 100644 GIT binary patch literal 2939 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 literal 649 zcmeAS@N?(olHy`uVBq!ia0vp^2SAvE8Azrw%`pX1LIFM@u0ENCFP=Vq{^ZHa=g*%# zeE9Oki|r@bT7a@FB|(0{49Dy26;{rd0`fTvJR*x381$4un6YB1eHjA-6R)R>V@SoV zw|7tXH5&-H1pfN^f4|MPgN$nwv$b~Z?P$62Wn<%nPbW4n&Aor>UR~Yix7B<9+I*dT zI4z4=?)1i!y*rNn4R}9m<>^%UrR+C0o%1wsZ3urVGDT?t?{ASEY74g3a`*^e_$}A# z-pTY!CUnd5i5dy-@BAq+nK=9AFV(-_W-lnJ+2(z+J$=HGyr!F5WBHw4 zszt1hb=&@(NB4lt?+Y`Umw!vmdK>@QQtQrtCXT{}C$9xu|0Z>O`;`--I``Xh9)r)b zFMig2{`TRXGdyeTwGSP=#{RA_^~KWZWwquXgM8xO%KSU!YgjhxZ~9bkrazbWXMD@r zTz`~PVxe!0kk3x5y5utq!NL>f{|Q=puB7Pwo$p`#pSe~ZtA1OW(f?PcwQg;jt?N|1 zNn82?KJA~hgEdV;QjM!(#}u!kuSc#hb}NKVzu&_6T{D0sK*TjrkN@}^3;sI`XvQwIW&A6-ns>!N*JZK52%;UZV0>h~ zZ`QwQTR+WaJDq=9XMf?g)%mga>|4$I=l`#F$o>7#YTNtof0!d5cSdpUTeJ=25>Hn@ Jmvv4FO#oRk3(5ch diff --git a/examples/starting-top-down/assets/StartingObstacle.png b/examples/starting-top-down/assets/StartingObstacle.png index ccba3327451c2e99cae0b4f3e69ccee006154732..4b3c516f7482a24f64ff6ec2f5c3c1190be97db0 100644 GIT binary patch delta 1791 zcmV<6QHn=LzaXk2o-<>-nKi<3Aw&HhwJNe$G82=yC0BJ$W5ka z9>`k~MBfr(8J2v4JZ9EB$&tq% zzJJ|VfURXf(EEf{4gP{rV#x(u9ahj*0AsK%x{FMIkU>sAXjoOLc4!5gt-l=w&>kKH z^!`0VcV%uW05>o<;T{mmLKRqVz7U)TuJkx>K-{TSFP+7gL|{a2@Z5QI=lDl~Kj8mh4O$4z*G{?{|}?|(p-usEzK7z+v&aEWpMI0@f^;)1J-px{VZ z!qp^O^t()gQ}j2kP;@A?0>;n<1X&#F{c(n)@FFW(-FH8!ZiU~-j4QSvE(o0&x^MU@K*3<*-k>D>FX2#VcwL}qDu47o zd<+SY!=P!<^t~CA5#x@gO$n}YQ*mo^Jei)JhjEAU2Yzs1#|pRkv0sCU)0M09zo2DWpe`vRLT$xKh7B}F43V_6+s10dwHuVU`jXCVNz>JZlRutSLH~eOT z7E?GD%wR%t9H0@Ogii+M2zVv`tbb%U^8=0p?*m#(JaF{Tc$I$ijokmrny%lRAZ_>w z?g5%MDV;W_!b^H}6yQ`~2Dky$^28k`i1@lNT2-fQsn8Afgb!y*?89SKl?&XQYEze+ zZdFt&ATUKYCNVCEGgLhyfdRFfnXjz?%9vtq@QvusWO5qf3RRcM_&>_HM}MP#>wTwV z2Ur2c1oTC5WO`PrS{Hj?E#3a0Ljg@I0AZ2g?A)W+g~v0JMN>{)@vYt`JZprCM=z;J zA_G3eDOW(kPtQsPX+~~k|7rJ!o^u;#397C0UnV=5m#;@|2_b|xc{Q=-b-_~s6dnh; z0tx?c;ak`IhN}o#6Z99uB@^2M|?#C`P zMYHJh(uX=ipBbWROShnER0pN}FrCqU^@n1Zn|eGnmDL$<2rEj53qRyl^|jwrCnD0Z zIjSpmtwD#;&{MD9rg#qrC$l=(^{iWar^9e)u3T~PcDwGk7y5W~mK4#*4A>$JTF&-E z@D>9*JPirR;=>oC9JX>Hq2JTKWvmZR`{7tdqrODNSdK!NbA+B|5WTCZLAg4d#0?H` hOsBs3c}Uui{{dh3#l7X3BwGLg002ovPDHLkV1lUtKTrSw delta 490 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 delta 736 zcmV<60w4Xt8tny;8Gi!+005o0f$RVP07y_wR7JCF1>A!Jzj6ezY6kI+0iIw7%XtLx zj{&D;2Gf26nqCM0z&Y`c0jy{SpkfE9X9uNZ2mk70^OzUQf)V`HQ}V4Xx^)iMnkS}j zm+Sxl019+cPE+bz*Z;oiIqJj*XNjkQ00009a7bBm001r{0Dk~3BLR4&KXw2B0yRlQ zK~#9!&6i1%gdh+_F|f;lu|V+tH*G;tX0a-wqrV$B`FSKmCScn18e7%->P##!;Q($T zPMmQgYBihh6lD^h8ixL53|=FIrvWqKo`Lc3#XSd%HwQ57@uXldM+`Ib4TgRKF92f# zRw0;Y9XNErO@E-3AVk2uGiWEEVHN;*N)Q?h&Y+zjNP zu$5KzEPs4)m@ENnKh7e5AGBU5izwZRP~89&uQYgjsp9KWRh7Cx3X_nWxNCh;)ykAl z!^*F^CT?nDrnD$&>nqh5H`GQ`T%+N&E3QQ1mg>As!>iA$WLJNXKomL@w(W>@Ri?v$ zENrnfZt76>4rlwdEe>sHaoW2ym@^=M*(*2niCO%g_sR_f^;MXo9E|l|`EC_Hcr)?n z(ba*6If$Qq)A)=adb-{|ePaTeK7E77_Z>kn{_*(U)QtfAq+co^hf968#ZSNMt+Mrr S%RRsV0000``W z$lZxy-8q?;Kn_c~qpu?a!^VE@KZ&eBK3|DzL`iUdT1k0gQ7VI5W_oVoyp7Y685o$J zdAc};R4~51?wfTbL8jqjy?(?#KKYDKO|Zt5ykCl!o#sE^o?(1aCI4aVC$+PWdUnzvzv0P z^_03LrZ1N0U;W~@U7pkJtrxfDF4=Esw)8~zid9*Qljo#MRA1Cv^m^CnIoFn4es=ol zC*R8^`~U5{chW8Pdim!)HB2?`Wc@Sj~6neM(*<|&y!sq#&S2SH0o_UkMzwHqn*>={gcnkT={5K z4NJ`I|1~UguB{C_{bgTx-}g(V#}#JBe6apq8) zeeU_^`^WVgSBHML`#O-mY1OJ* zFC?Bz9*f)3{bJWUqvcEgfBB{V^3Hj7cJ_FG`5)XReG68o?QuB{%nJ;ju6{1-oD!M< DdI3mz literal 150 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 x+VlMVl>qr)eza@;{7jZ}^1s$%`qD$R`yp&Kp~ z^-1F#0b+&^?~!djl}Lah%Xi-SRd8Jo{?;3o-h)_@@SoEI=)F?aM7l^>yqk02c9S24QiaO4jwr%c}IEn=$@Iu|j z{A?dLBatx*q%rq|JS?4a|Lt#kE6DC0Rls21az!T6sTB=BB|cf0=hn7W5Tj2YYh_UTYzD|O1qCGKoBgnDQ^!7 zlxOgcU=(L3SI*wW5@3SY9PjRFnyg5Rw>yp@`_0$geX&9y2t^gk=3+ldi57KzAFG}Z zt#+FP$a-;l>dnP*u|mK!5AdGgZ(`akjHzkN;Zof2D7M=ex~}UY`H(K9HEdOE2BrL~ z=3J{pA`;v$; r5yj*z64LN`uAO!n*j9V(G7kR$?WdIAZVN{t00000NkvXXu0mjfzF)6> literal 0 HcmV?d00001 diff --git a/examples/starting-twin-stick-shooter/assets/Flat dark joystick border.png b/examples/starting-twin-stick-shooter/assets/Flat dark joystick border.png index 606bf85b223d68dbdd1be4f5ac851f2018e13828..b4e83e112eb9375cefbb86a6d044a972f8bf4dcf 100644 GIT binary patch literal 3751 zcmV;Y4p{MtP)005u}1^@s6i_d2*00006VoOIv0RI60 z0RN!9r;`8x010qNS#tmYE+YT{E+YYWr9XB6000McNliru>IW486)D;&{k8xA02y>e zSad^gZEa<4bO1wgWnpw>WFU8GbZ8()Nlj2!fese{01gjHL_t(|+U=cxXq@+Xz(3D> zC+SX79NUp>$yVSJ$>iiJ))lIUFb5nqkA@bSF^@SUfk8d!SOYF+9(=fJWpSxPJu{Ww|g-LpUR+CJNoe0RD#-Fx?Z zz*x5LUcdLeAD-v?eDC{yUoebKL@WgNh(uz+rWQAX1fn@1BG3aG0qFpq0NQ~LAn5&^ z&m55PexCtuflh)EfITSm(lNuxSW*0eB9GRYA65q#Of&1Dq9i`D~*IS=R#gJ@af2 zj`KC(dEj6T3HK;6z=yyua;ooNPMlAhfPRrKIz9et%)c z1gyd4AsE(!z+VD~e3a=*$&6<}hm+&y&zOL1TFX~8+}1|tj&1;^r>?WOFkkB#@jqOZzpk}x=+so!i|#vw z1TNINd`->Gbad{#`{yS*chc0{TV(oQ32JDOOiy-oAe7JDH zah!rByuFkmm&@Tpl8C;TyjZ>UhgB1>w|{Uz;55*KQOisyyp$Rr9jluX+#4Mn5;$k_ z`GOvSbG^~(%okQ%z^D5M4~lS_hwmaYYL|8qPCwm0c+l4+`04(^gASa+RZjp(-z>5~F8*BcRuCoU^i9H_Rz~wGxd<*~fr=(R~rG+co*> zX-o*G`}XdO)N}^ivp3eRsvpyMHwZNfNmTwInTVHdrFGddU?ds~A~t>^m~ zn{H_|GKhY8qw5JbRDvVXSO}4mCSOAmLgiE>8VhZjfZj6t0lO9rOVo3q`X#uxe{e|P zf9x6m?aypfs3UgP&wha;CZDmv zj`(QFC0O8F7DHo$yuRMIBhK38ci%y5!GW=H`n>1(6`27=Ubom8W#VX2O-OJ5vmJtS z#kyakOt`3YuGWmKo&m-AhRJ7?ksvDzSqdw`qI}b0W|Wz)7gU11ZFE1icp2qJbGau| zpIn$&KLd&!vQQb7Kpf|5g_Phwwon-r;)Qif@SZ)fb`9#ZH6TW%x?yo;+oenKxm7cu z26V{eGb-5)Yfy*QF2TZ(#mJ~;8GdjK_`Jo(s4&m1ItKK{e>d^Tv)hdN7zf_XfEvv0 zH~EZei~|YifmKSd9I)6J)j);S1Z2O3#;6hls|omw#l@%+U%IaeiNs>T;F2!d7|^Iz zBjPSfIGv8W4nR{T+h_6_)oet9ExE|u60G9bvptOJA`;CX14b+&Ms@uok}se=NZP0> z3cK?JR1?ssHc#aVD7NjIQEfW%OYnwwKiIN$8#}so<2a6mwoY*zht95DJicvvIS%&( z+?#rNqV&O}u)T99ZEY)$2X1Z&ky%`@t5t_Iw}c3XTM36-2?m?^eC7tKTI%82^93yF z;nGeZKsHwS`?p;rsd<{%XC`9+JCuwSK zF6HgPd;yEffZ^6Qc6Rp=2-=q?jZPpKq-$3X;kKfSXnVeZ1rus*rE_PuH5*1=AK=ir zql-|uwfJFN(eF?TxUrUqR9?W$;sP@_uA`c>h#LUpayh1_uCutXP_lq?#k^@@exAvz z6J%Z2qTk?TU6-kA6D%&w7yH)x?g=T{q~+z?T)%dOrA)@ILW8ojl;QfdD=aVHF6Hf+ zd;w=lO1;@EHzuz#Klg=QjRs?3evTX0ua-IocsB#ynMau~^(d;!XVX)-?lLVcA#N{O zMyywEFJ;IqF0zza;MVNTjUH|$U%;7-9_`l6&+Q5|K(ldU@@lybcPp<1>x_lhs4|mz z0!rHAVN{tbcLh|Kuy`0%=IXr+sA?I|s4^?gIRK85uz(m<)lW&@6>vH1q$y!C+AvDQ z&1Rf?0;c0}7c2`JRU?s3$KAXx7=YhdT#PF5$(W|=QDMf`d`<|$Z!JDXh51eXAMO`0#|IW4qr#kB zQ$WeeSsMo$6(l3BtSz8dn}20-GAhJ}`QyKbOYjO-hK)+`iwFN=6#;WUw>TMPYV_>nTXE< zzqGg+W#UX~JU&x+23$#*CoFD8nHXN{AJ!Dm4apf;SvGAL8z7w=A3amFfZi(UVT+%! zLEc&KAJ^ZWP2~hIYcVuRWY%kFOB2v*Ml1^&C86+R&!G}CARsDl<87s77)6jl<@lps z^hg0OCE}Bo1&tE2-*9Thos9|TWx?Z?1&wlNzk&tyvfy_uu0}btU%>);9r4@N5gP?C zDJpLj`r<&LFZ_IFGPAw2>lSdxVr;C#-y{>G9~S!Jq8`)Ww#W(W26kg@;_f5n{Wc_E zIv#gbdClT%tU5|l-b^RrnTiPrkV=e>06(;F8xJt-xj{t*^cs?HW4o#vdBxFHIabOW zOA;`dh|em#X>m6I6#k;v(pz*F{2(`HrhdP@v#T4}ZnB=M#!e%{1V+@3mASzTxT@D96d% zoS8bmy|b$m_=;VchTvaQg4 z1#_zM)l?#0_Jt@;CC)Z=@%)&=%h>cxJ&;l5~~2K|5#ML)^+@9v`f+2v$>q1ykkZ04u#>mPg-c-Jo- za>tG?THDs|qvq$n;Iru|-*mbPucgLE|8uiWKsGx9y?ggXMdc(gQ0rGqMA*^2o0c`k zf=ihUHzu#5s(z3!p(=;Vv82jZCHO&ZPEXBj>FEBEBXr_}<^78b^R%@+hU45nho5y_ zrY5hV{%AgiU6sS17Px&zU1z}ivf;{n;g5hwEnmH+re=2T>bWbSYL3aP6D%*^u66My zRCzO%7=6EH#@4kIotraL=`9`I!?m6bbGayN!>PpR`I;Km z)M~VPqk{)T_yG{B)pHEDwxSA|#f4fHZ%mcfO1Wvgp#=npL}Q_#=n>!_fG&(;m<9gM zU6d2)bi8ICIwXFGq_@)gE#P%*QNyFi0N+LB?P^ad*R6ne*b|L)spxl1#MQ-97)IrI zss_5-x)g8~5&s<6YNeK8Re9S_;`v&@{L-wVM+Ck_jZfWFfF#N=$O%7`+PW0*ei;W3 zBD?|oQ5~0O7x*R02{$8WYPq>+I05g_+aK#d^&2956F5*OA8Q2o87lAjIomaafDe@D z2%?98KLMWeQD$_3UjZMGlb37exR7 delta 928 zcmV;R17G~79jgbB8Gi!+006uZmmdHC02@$DR7Ff_aWyqHHa0djH8nOiHZ3hJHa0dm zI5|R^?&H|1(F!EEFDZlRnwi_ z{vfi zj=HzjIAJ?^*aIR1yr3o5f0`Hd%5^<6F6Up zP~Fjl;*KVq#|bI9VKHMl8{r`)iPZ%{-xG$jf4=?q~^F0)IA24NSmi2{R2agHyOAVdeoQ zxPbQ(79!wkIWZ#NI!%xsUi&yP0Snv1t6i5;0fp`1zdpu#*d3k2|9q@mKxK#E_j(Kt z!LJ|`GN7)T^niore1Z;j$NmwypId1iKp=wpMU(OX1$5-Pn-+2DQ3 z3U8HfoorJ0VFQ$enSS6!7WRpkSLewMoFS+@5$ws+?STP?b}+=r0-O&JIbb}yTqpDq z_kD-lXD%rF#snxfL8d2)U`M4yN4i*I^UWcQjl?nfK%C*3a$s;&f%6R|JUAMx!11?+{;EmT?0T5AdBGkQj7xl&{aQ(}c)V~^X*e%3^Jziw;PzI z-Q)Pxrd*^ diff --git a/examples/starting-twin-stick-shooter/assets/Flat dark joystick thumb.png b/examples/starting-twin-stick-shooter/assets/Flat dark joystick thumb.png index acd005cc067032996264e510b58c8dbc0c102b62..8acf179f8660be36ccea1198105da35c07154fd7 100644 GIT binary patch literal 2939 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 Z&aqn}>0zSUtAH+I@O1TaS?83{1ORyvaZLaK diff --git a/examples/starting-twin-stick-shooter/assets/StartingPlayer.png b/examples/starting-twin-stick-shooter/assets/StartingPlayer.png index 2bf62a0adf55e27d9c4e4e83e68d06e7d4633d37..0ba05892c27e692f74ecc6720078c5f280e1dad7 100644 GIT binary patch literal 3394 zcmV-I4ZZS-P)IWABEC$yzJBI)O49`hK zK~#9!#hYo59a(wDf6u9^yLGqSw!6Iy_SlR)#)E-oFiR2<5GY0g5oJJt#3(@^5TAor zk@69Efy784qy!QTfsllRgb4y6LM8~qj5UPCv)SW?X?Nef&Utuo>fT#bx2kU4_Si0| z)m68abDn)U|0DQAzWA}`#m~Nb?XPcl^@C33kSaI@P5vB$a|+4tnR^PVf~wBW0d=5& zbN-pacPG!K?vecN-0ZpOeFu#Q)>q-=Vc0mRsP*?3A9~Yt%>O*`gZ|%q`ib4A0OZma zXgsvG@!j^m@I5!K#TfB}Km`M)lamw!6ji~grY0>30~Du{d6$G)P7R8pt{wyYe_=R+ z*uoo6Dd!)m!2|^aVh4?*0li!H$1gp5iHN$w{pd$wpYk{QN`&}qRlzk?TE0u$9>fPgWeLv{A5{;7!_ zAei|1oOzEAnS!#Z!I#p}XnxiY4rdlerUsFmo=W8VS7K8z8cfA?Z=Rhd~ z7TKR9lF>vGQp}V*5oO<81p_Hr=QnldiPTk7&lQ+J=6zGD z&U0o^#kkbD2#j3ags6}KQW1K}oKo7I=beKt;Y2HffR;re$vmVAtzbbYuV#Q2PE7N* zubL&Kl;tU>sq?-e!#Ayp0wrdc8`nB$w;(V}Ge8zO$`a=$#mS4LnK^NgQZEAJJ%2_i zi=vWDPsg!xbX}2fIWsT|0&;~EwBS5t=#mSKNsTZeP_@R8)AXX?A)VzO)(XuCj%|?S z()EvUkvKG+yr3n1Kz&J<5}7(stHdLShlwIY{@RW~0!2bWq5{9R3=smuv2ymfvTr~2 z>Va|w%)9OBnE*N=R?qVTW3LImOjGbSf%BLs#2)(>c9>Z(-985DPrZEp>^0`A~2E6 zKA%{J#)*dXVf+6rH!eADT(USIY{^_kou8w%Fc`ywN0fJ-Qv$&!L3?L_mTJN5c3R@8 zOl;s#g#8x+*54k|+%(vMqGNDla3d{GMI+dJTgc`GW&2klSH2f>{TG(7BS?^UXg($l zM$l=&qYo=5jsbRW!~@9fzunMDH%AwAdNOreJg;nV*N3rF7em%g8h-xe5yP$$wuQj}nh{)hSUI}s9i<&l=oJ_Z{a+A5 ztG$OrNYdWEBK#%c&GnSlser>DX%aO3^`3~&SIQO!uPO)67`8swAZ!cW9q;Jg^Hxv1 zj=joHhZ~LH$OD*9ejeF+7j$=OCj;SJiYgW9XL9ZaN_fz)@fQumEFXI69K+kn`YFTt z#~a-G=8#7o#*bBGUN*)&vIP&GMUI_7cemm4C0JeCOBgJ2eyK|f5(oz`Mn0_dxogMp zwsPpa;fM{9r^mRKl$0xp=Jt2MStMbhd)>(Ds^h05F0* z7b31k*G>cm16W(beCXp4HN11I-4Bp*2jxc4Iv)}pGU)i;(2oFP(4ztFkb!Xx#*09& z2giw?<7TSTC{3#ZOt-upoq4U&4L zk^x$}q@osrbs<=jJ>}eYtO#Rp#{wi=GS>EXVe6gn$M1)3caB6;Dg9I}t|N$!iqGrs z$6*!i2(GncwWtb%F6NKk2XEYh-cD6Vs3QEwO1K_Cp!HPnZX5yVzF_&zXKZ0K!2H=? z!t2j@tyr!p>IV;2?vwP>8rjB2gQeaRh<)B{KJXZ99)P=xvMv8 zrv6}_x0qWF>ZIbTu(AqQevLl$uioJqQ?neNQeDs=G(Hq||IZ@#swN0QhDz{EjMgzd zRp_k1)faL9{P*4r!l-UPAiWw<43Uu*^q#Q{p0k)$xx1O6csN`0*_dG0RU@Dz3E|B) zba!yy{2C1V&}h`@2$5RV&1*tB2rFXzy58@&%ul}+GY+K! z2A4ocJNqCW%E2vQbPRopIM%J za?HR9yT5>~)5`if=qNX}Fv4B>DJBST&s~BSe&@}gMr%A-87e`CmLf&{we3N$X4v>} zMC*96Fmfl_pb#`5>y5QamKLOplFappvl7mZVn-Nw-GL8ArDX6 z0tOTO`k z?kYDgS+0EFvh$)NT1kM`>2VT(_F$x#0M5P+&Ky_5FsBX6GoWg5v~`SG z=JpxG%ISd4Nkh11FvdrtsmrwqF)<8Zb-a4n@ygSlaA^uAECWl==4qX0#QG}y$$6#I z@z{gAqya^UZz*`rYf0SqE9j;P(E&qaBMXoi_hEb+dN&;X8;)_`OHZ0o43~!~oIb;J zg~1S3Tk!r1N~7Udu9i$Qs+s_rX^3Rv?GziBMuU0mX^<3K&cWbi$LRD?#T+}!btHnJR8y2eJEjLJ_SuYZF(RVdlyBL9P5A16XmeykKt5UdyMl`EAD)Sm4GQVR)7WjZthcH8n z#(GC!>4hWbh$rg=^R}tgO%_#U@v=NQpkkJZ>8e##XxtfCx{P?qsd53dJySkQR<&h8 zwOKafIrpIzq0uL1&BhXYhH7<$ih2WDpyK&WQR!%qL5df&(uy>tX#YUP3W=bMVqtT? zuPK%TOKTksAPbbtY@nE=YKcmU%u9J$!_;bN=S|Kj1V%Y}VA*lNB%3=I>!47=i1daE znK?(%V!xF(VcAO$LFY_vY=tB1uyJrXys&~AW7w`=#cN6EOZ`bfKd~t5De1h;H>1jZ zh2X=UsPg8Abeg5tJRAlwtB1|Mw;Cc2(OQg<{NA7v&5@FAb5fAol$v+S)9XBXElw)J zx13fs4%Ms%eG0D95EDEQJ`wyf>i)yUW1R;MZHA9_cbpNKg|)1kN$r+VpM7QSrLwY_ z1N4l@yqla)T}LiC)CBYAr6Kul+_e2S90_5lTsWhgez3mkTZQO>&~A6#*FN>_S3Wc4 z|Cj!1?G2xPNBi@)cJ&>i9CoNWm)ZiK%6+U&P2NGQ;&I$ol43_wyT^Si%Xh6bVZa1{ zEy+%Go!0x)k~e9)r3My<;Q#;t07*qoM6N<$f(hAX)c^nh delta 736 zcmV<60w4Xt8tny;8Gi!+005o0f$RVP07y_wR7JCF1>A!Jzj6ezY6kI+0iIw7%XtLx zj{&D;2Gf26nqCM0z&Y`c0jy{SpkfE9X9uNZ2mk70^OzUQf)V`HQ}V4Xx^)iMnkS}j zm+Sxl019+cPE+bz*Z;oiIqJj*XNjkQ00009a7bBm001r{0Dk~3BLR4&KXw2B0yRlQ zK~#9!&6i1%gdh+_F|f;lu|V+tH*G;tX0a-wqrV$B`FSKmCScn18e7%->P##!;Q($T zPMmQgYBihh6lD^h8ixL53|=FIrvWqKo`Lc3#XSd%HwQ57@uXldM+`Ib4TgRKF92f# zRw0;Y9XNErO@E-3AVk2uGiWEEVHN;*N)Q?h&Y+zjNP zu$5KzEPs4)m@ENnKh7e5AGBU5izwZRP~89&uQYgjsp9KWRh7Cx3X_nWxNCh;)ykAl z!^*F^CT?nDrnD$&>nqh5H`GQ`T%+N&E3QQ1mg>As!>iA$WLJNXKomL@w(W>@Ri?v$ zENrnfZt76>4rlwdEe>sHaoW2ym@^=M*(*2niCO%g_sR_f^;MXo9E|l|`EC_Hcr)?n z(ba*6If$Qq)A)=adb-{|ePaTeK7E77_Z>kn{_*(U)QtfAq+co^hf968#ZSNMt+Mrr S%RRsV000082q^@F&>|q1 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 edhy=ZUaI$;!{ literal 0 HcmV?d00001 diff --git a/examples/starting-twin-stick-shooter/starting-twin-stick-shooter.json b/examples/starting-twin-stick-shooter/starting-twin-stick-shooter.json index 96fc4af95..5c2edc031 100644 --- a/examples/starting-twin-stick-shooter/starting-twin-stick-shooter.json +++ b/examples/starting-twin-stick-shooter/starting-twin-stick-shooter.json @@ -1,7 +1,7 @@ { "firstLayout": "", "gdVersion": { - "build": 272, + "build": 274, "major": 5, "minor": 6, "revision": 0 @@ -146,6 +146,34 @@ "name": "assets\\StartingEnemy.png", "smoothed": true, "userAdded": false + }, + { + "file": "assets/starting_bulletfire.wav", + "kind": "audio", + "metadata": "", + "name": "assets/starting_bulletfire.wav", + "preloadAsMusic": false, + "preloadAsSound": false, + "preloadInCache": false, + "userAdded": false + }, + { + "file": "assets/DirtParticle.png", + "kind": "image", + "metadata": "", + "name": "assets/DirtParticle.png", + "smoothed": true, + "userAdded": false + }, + { + "file": "assets/Collision.wav", + "kind": "audio", + "metadata": "", + "name": "assets/Collision.wav", + "preloadAsMusic": false, + "preloadAsSound": false, + "preloadInCache": false, + "userAdded": false } ] }, @@ -272,6 +300,7 @@ "adaptCollisionMaskAutomatically": false, "assetStoreId": "", "name": "Player", + "persistentUuid": "82346a53-ea91-4ccb-b349-869c9cafc4fb", "type": "Sprite", "updateIfNotVisible": false, "variables": [], @@ -307,6 +336,10 @@ "BulletLayer": "", "RandomizedAngle": 0 }, + { + "name": "ShakeObject_PositionAngle", + "type": "ShakeObject::ShakeObject_PositionAngle" + }, { "name": "SmoothCamera", "type": "SmoothCamera::SmoothCamera", @@ -533,17 +566,24 @@ "adaptCollisionMaskAutomatically": true, "assetStoreId": "", "name": "Enemy", + "persistentUuid": "2e9d6475-8c2b-45e6-9805-b3e0a24f7986", "type": "Sprite", "updateIfNotVisible": false, "variables": [ { "name": "Health", + "persistentUuid": "e90b2220-1831-4277-8f82-ee32aebaa4ba", "type": "number", "value": 3 } ], "effects": [], - "behaviors": [], + "behaviors": [ + { + "name": "ShakeObject_PositionAngle", + "type": "ShakeObject::ShakeObject_PositionAngle" + } + ], "animations": [ { "name": "", @@ -596,9 +636,10 @@ ] }, { - "adaptCollisionMaskAutomatically": true, + "adaptCollisionMaskAutomatically": false, "assetStoreId": "", "name": "PlayerBullet", + "persistentUuid": "cc220ded-6019-4249-a367-c525c85970c6", "type": "Sprite", "updateIfNotVisible": false, "variables": [], @@ -626,31 +667,31 @@ "originPoint": { "name": "origine", "x": 0, - "y": 16 + "y": 32 }, "centerPoint": { "automatic": false, "name": "centre", "x": 0, - "y": 16 + "y": 32 }, "customCollisionMask": [ [ { - "x": 1, - "y": 5 + "x": 0, + "y": 14 }, { - "x": 32, - "y": 5 + "x": 50, + "y": 14 }, { - "x": 32, - "y": 27 + "x": 50, + "y": 50 }, { - "x": 1, - "y": 27 + "x": 0, + "y": 50 } ] ] @@ -746,6 +787,102 @@ ] } } + }, + { + "additive": false, + "assetStoreId": "", + "destroyWhenNoParticles": true, + "emitterAngleA": 0, + "emitterAngleB": 360, + "emitterForceMax": 100, + "emitterForceMin": 50, + "flow": 800, + "jumpForwardInTimeOnCreation": 0.15, + "maxParticleNb": 300, + "name": "DebrisParticle", + "particleAlpha1": 255, + "particleAlpha2": 0, + "particleAlphaRandomness1": 0, + "particleAlphaRandomness2": 0, + "particleAngle1": 0, + "particleAngle2": 0, + "particleAngleRandomness1": 0, + "particleAngleRandomness2": 0, + "particleBlue1": 238, + "particleBlue2": 255, + "particleColor1": "238;238;238", + "particleColor2": "255;255;255", + "particleGravityX": 0, + "particleGravityY": 0, + "particleGreen1": 238, + "particleGreen2": 255, + "particleLifeTimeMax": 0.45, + "particleLifeTimeMin": 0.3, + "particleRed1": 238, + "particleRed2": 255, + "particleSize1": 50, + "particleSize2": 50, + "particleSizeRandomness1": 0, + "particleSizeRandomness2": 0, + "persistentUuid": "e86ed3b5-d165-4278-ae77-27dd10970655", + "rendererParam1": 3, + "rendererParam2": 1, + "rendererType": "Quad", + "tank": 20, + "textureParticleName": "assets/DirtParticle.png", + "type": "ParticleSystem::ParticleEmitter", + "zoneRadius": 3, + "variables": [], + "effects": [], + "behaviors": [] + }, + { + "additive": false, + "assetStoreId": "", + "destroyWhenNoParticles": true, + "emitterAngleA": 0, + "emitterAngleB": 360, + "emitterForceMax": 150, + "emitterForceMin": 80, + "flow": 800, + "jumpForwardInTimeOnCreation": 0.15, + "maxParticleNb": 300, + "name": "LargeDebrisParticle", + "particleAlpha1": 255, + "particleAlpha2": 0, + "particleAlphaRandomness1": 0, + "particleAlphaRandomness2": 0, + "particleAngle1": 0, + "particleAngle2": 0, + "particleAngleRandomness1": 0, + "particleAngleRandomness2": 0, + "particleBlue1": 238, + "particleBlue2": 255, + "particleColor1": "238;238;238", + "particleColor2": "255;255;255", + "particleGravityX": 0, + "particleGravityY": 0, + "particleGreen1": 238, + "particleGreen2": 255, + "particleLifeTimeMax": 0.45, + "particleLifeTimeMin": 0.3, + "particleRed1": 238, + "particleRed2": 255, + "particleSize1": 100, + "particleSize2": 100, + "particleSizeRandomness1": 0, + "particleSizeRandomness2": 0, + "persistentUuid": "95b47721-951b-4f4c-86e1-d3f7f67c0831", + "rendererParam1": 3, + "rendererParam2": 1, + "rendererType": "Quad", + "tank": 20, + "textureParticleName": "assets/DirtParticle.png", + "type": "ParticleSystem::ParticleEmitter", + "zoneRadius": 3, + "variables": [], + "effects": [], + "behaviors": [] } ], "objectsFolderStructure": { @@ -760,6 +897,12 @@ { "objectName": "Enemy" }, + { + "objectName": "DebrisParticle" + }, + { + "objectName": "LargeDebrisParticle" + }, { "objectName": "RepeatingBackground" }, @@ -1021,6 +1164,35 @@ } ] }, + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [ + { + "type": { + "value": "FireBullet::FireBullet::HasJustFired" + }, + "parameters": [ + "Player", + "FireBullet", + "" + ] + } + ], + "actions": [ + { + "type": { + "value": "PlaySound" + }, + "parameters": [ + "", + "assets/starting_bulletfire.wav", + "", + "20", + "RandomWithStep(0.9,1.2,0.05)" + ] + } + ] + }, { "type": "BuiltinCommonInstructions::Comment", "color": { @@ -1050,6 +1222,46 @@ } ], "actions": [ + { + "type": { + "value": "Create" + }, + "parameters": [ + "", + "DebrisParticle", + "PlayerBullet.CenterX()", + "PlayerBullet.CenterY()", + "" + ] + }, + { + "type": { + "value": "PlaySound" + }, + "parameters": [ + "", + "assets/Collision.wav", + "", + "30", + "RandomWithStep(0.9,1.1,0.05)" + ] + }, + { + "type": { + "value": "ShakeObject::ShakeObject_PositionAngle::ShakeObject_PositionAngle" + }, + "parameters": [ + "Enemy", + "ShakeObject_PositionAngle", + "0.2", + "RandomWithStep(-5,5,10)", + "RandomWithStep(-5,5,10)", + "RandomWithStep(-5,5,10)", + "0.05", + "", + "" + ] + }, { "type": { "value": "SetNumberObjectVariable" @@ -1088,6 +1300,18 @@ } ], "actions": [ + { + "type": { + "value": "Create" + }, + "parameters": [ + "", + "LargeDebrisParticle", + "Enemy.CenterX()", + "Enemy.CenterY()", + "" + ] + }, { "type": { "value": "Delete" @@ -1113,9 +1337,82 @@ "", "" ] + }, + { + "type": { + "value": "BuiltinCommonInstructions::Once" + }, + "parameters": [] } ], "actions": [ + { + "type": { + "value": "ShakeObject::ShakeObject_PositionAngle::ShakeObject_PositionAngle" + }, + "parameters": [ + "Player", + "ShakeObject_PositionAngle", + "0.2", + "RandomWithStep(-5,5,10)", + "RandomWithStep(-5,5,10)", + "RandomWithStep(-5,5,10)", + "0.05", + "", + "" + ] + }, + { + "type": { + "value": "Create" + }, + "parameters": [ + "", + "LargeDebrisParticle", + "Player.CenterX()", + "Player.CenterY()", + "" + ] + }, + { + "type": { + "value": "PlaySound" + }, + "parameters": [ + "", + "assets/Collision.wav", + "", + "90", + "RandomFloatInRange(0.7,0.9)" + ] + }, + { + "type": { + "value": "ActivateBehavior" + }, + "parameters": [ + "Player", + "TopDownMovement", + "" + ] + }, + { + "type": { + "value": "ChangeTimeScale" + }, + "parameters": [ + "", + "0.15" + ] + }, + { + "type": { + "value": "Wait" + }, + "parameters": [ + "0.15" + ] + }, { "type": { "value": "Scene" @@ -1225,6 +1522,10 @@ "name": "Scale", "type": "ScalableCapability::ScalableBehavior" }, + { + "name": "ShakeObject_PositionAngle", + "type": "ShakeObject::ShakeObject_PositionAngle" + }, { "name": "SmoothCamera", "type": "SmoothCamera::SmoothCamera" @@ -20857,6 +21158,4479 @@ } ], "eventsBasedObjects": [] + }, + { + "author": "Tristan Rhodes (https://victrisgames.itch.io/)", + "category": "Visual effect", + "dimension": "2D", + "extensionNamespace": "", + "fullName": "Shake object", + "gdevelopVersion": ">=5.5.222", + "helpPath": "", + "iconUrl": "data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iVVRGLTgiPz48IURPQ1RZUEUgc3ZnIFBVQkxJQyAiLS8vVzNDLy9EVEQgU1ZHIDEuMS8vRU4iICJodHRwOi8vd3d3LnczLm9yZy9HcmFwaGljcy9TVkcvMS4xL0RURC9zdmcxMS5kdGQiPjxzdmcgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIiB4bWxuczp4bGluaz0iaHR0cDovL3d3dy53My5vcmcvMTk5OS94bGluayIgdmVyc2lvbj0iMS4xIiBpZD0ibWRpLWFycm93LWFsbCIgd2lkdGg9IjI0IiBoZWlnaHQ9IjI0IiB2aWV3Qm94PSIwIDAgMjQgMjQiPjxwYXRoIGQ9Ik0xMywxMUgxOEwxNi41LDkuNUwxNy45Miw4LjA4TDIxLjg0LDEyTDE3LjkyLDE1LjkyTDE2LjUsMTQuNUwxOCwxM0gxM1YxOEwxNC41LDE2LjVMMTUuOTIsMTcuOTJMMTIsMjEuODRMOC4wOCwxNy45Mkw5LjUsMTYuNUwxMSwxOFYxM0g2TDcuNSwxNC41TDYuMDgsMTUuOTJMMi4xNiwxMkw2LjA4LDguMDhMNy41LDkuNUw2LDExSDExVjZMOS41LDcuNUw4LjA4LDYuMDhMMTIsMi4xNkwxNS45Miw2LjA4TDE0LjUsNy41TDEzLDZWMTFaIiAvPjwvc3ZnPg==", + "name": "ShakeObject", + "previewIconUrl": "https://resources.gdevelop-app.com/assets/Icons/arrow-all.svg", + "shortDescription": "Shake objects with configurable translation, rotation, and scale intensity.", + "version": "1.6.2", + "description": [ + "Shake an object (position, angle or scale).", + "", + "It can be used for:", + "- Hit or impact", + "- Object slowly rocking back and forth (like a ship)", + "- Simulate engine vibration, earthquake, or pulsing", + "", + "The top-down RPG example uses this extension for damage animations ([open the project online](https://editor.gdevelop.io/?project=example://top-down-rpg))." + ], + "origin": { + "identifier": "ShakeObject", + "name": "gdevelop-extension-store" + }, + "tags": [ + "shaking", + "object", + "effect", + "shake", + "scale", + "position", + "rotate", + "angle" + ], + "authorIds": [ + "gqDaZjCfevOOxBYkK6zlhtZnXCg1" + ], + "dependencies": [], + "globalVariables": [], + "sceneVariables": [], + "eventsFunctions": [], + "eventsFunctionsFolderStructure": { + "folderName": "__ROOT" + }, + "eventsBasedBehaviors": [ + { + "description": "Shake objects with translation and rotation.", + "fullName": "Shake object (position, angle)", + "helpPath": "", + "iconUrl": "", + "name": "ShakeObject_PositionAngle", + "objectType": "", + "previewIconUrl": "", + "eventsFunctions": [ + { + "description": "Shake an object, using one or more ways to shake (position, angle). Make sure to \"Stop shaking\" before starting a new shake if it uses different parameters. ", + "fullName": "Shake object (position, angle)", + "functionType": "Action", + "name": "ShakeObject_PositionAngle", + "sentence": "Shake object _PARAM0_ for _PARAM2_ seconds. Modify position amplitude _PARAM3_ on X axis and _PARAM4_ on Y axis, angle rotation amplitude _PARAM5_. Wait _PARAM6_ seconds between shakes. Keep shaking until stopped: _PARAM7_", + "events": [ + { + "colorB": 228, + "colorG": 176, + "colorR": 74, + "creationTime": 0, + "name": "Shake Object", + "source": "", + "type": "BuiltinCommonInstructions::Group", + "events": [ + { + "type": "BuiltinCommonInstructions::Comment", + "color": { + "b": 109, + "g": 230, + "r": 255, + "textB": 0, + "textG": 0, + "textR": 0 + }, + "comment": "Start/Reset duration timer" + }, + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [], + "actions": [ + { + "type": { + "value": "ResetObjectTimer" + }, + "parameters": [ + "Object", + "\"__ShakeObject_DurationTimer\"" + ] + } + ] + }, + { + "type": "BuiltinCommonInstructions::Comment", + "color": { + "b": 109, + "g": 230, + "r": 255, + "textB": 0, + "textG": 0, + "textR": 0 + }, + "comment": "Pass input parameters to global variables so that onScenePostEvents can use them" + }, + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [], + "actions": [ + { + "type": { + "value": "ModVarObjet" + }, + "parameters": [ + "Object", + "__ShakeObject_Duration", + "=", + "Duration" + ] + }, + { + "type": { + "value": "ModVarObjet" + }, + "parameters": [ + "Object", + "__ShakeObject_PowerX", + "=", + "PowerX" + ] + }, + { + "type": { + "value": "ModVarObjet" + }, + "parameters": [ + "Object", + "__ShakeObject_PowerY", + "=", + "PowerY" + ] + }, + { + "type": { + "value": "ModVarObjet" + }, + "parameters": [ + "Object", + "__ShakeObject_PowerAngle", + "=", + "PowerAngle" + ] + }, + { + "type": { + "value": "ModVarObjet" + }, + "parameters": [ + "Object", + "__ShakeObject_TimeBetweenShakes", + "=", + "TimeBetweenShakes" + ] + } + ] + }, + { + "type": "BuiltinCommonInstructions::Comment", + "color": { + "b": 109, + "g": 230, + "r": 255, + "textB": 0, + "textG": 0, + "textR": 0 + }, + "comment": "Determine if the shake should keep going until stopped" + }, + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [], + "actions": [ + { + "type": { + "value": "SetObjectVariableAsBoolean" + }, + "parameters": [ + "Object", + "__ShakeObject_ShakeForever", + "False" + ] + } + ] + }, + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [ + { + "type": { + "value": "BooleanVariable" + }, + "parameters": [ + "ShakeForever", + "True", + "" + ] + } + ], + "actions": [ + { + "type": { + "value": "SetObjectVariableAsBoolean" + }, + "parameters": [ + "Object", + "__ShakeObject_ShakeForever", + "True" + ] + } + ] + }, + { + "type": "BuiltinCommonInstructions::Comment", + "color": { + "b": 109, + "g": 230, + "r": 255, + "textB": 0, + "textG": 0, + "textR": 0 + }, + "comment": "Add default values if none were provided" + }, + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [ + { + "type": { + "value": "VarObjet" + }, + "parameters": [ + "Object", + "__ShakeObject_Duration", + "=", + "0" + ] + } + ], + "actions": [ + { + "type": { + "value": "ModVarObjet" + }, + "parameters": [ + "Object", + "__ShakeObject_Duration", + "=", + "0.5" + ] + } + ] + }, + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [ + { + "type": { + "value": "VarObjet" + }, + "parameters": [ + "Object", + "__ShakeObject_TimeBetweenShakes", + "=", + "0" + ] + } + ], + "actions": [ + { + "type": { + "value": "ModVarObjet" + }, + "parameters": [ + "Object", + "__ShakeObject_TimeBetweenShakes", + "=", + "0.08" + ] + } + ] + }, + { + "type": "BuiltinCommonInstructions::Comment", + "color": { + "b": 109, + "g": 230, + "r": 255, + "textB": 0, + "textG": 0, + "textR": 0 + }, + "comment": "If duration is less than a single shake, increase duration to make 1 full shake" + }, + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [ + { + "type": { + "value": "VarObjet" + }, + "parameters": [ + "Object", + "__ShakeObject_Duration", + "<", + "Object.Variable(__ShakeObject_TimeBetweenShakes)" + ] + } + ], + "actions": [ + { + "type": { + "value": "ModVarObjet" + }, + "parameters": [ + "Object", + "__ShakeObject_Duration", + "=", + "Object.Variable(__ShakeObject_TimeBetweenShakes)" + ] + } + ] + }, + { + "type": "BuiltinCommonInstructions::Comment", + "color": { + "b": 109, + "g": 230, + "r": 255, + "textB": 0, + "textG": 0, + "textR": 0 + }, + "comment": "Detect initial shake" + }, + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [ + { + "type": { + "value": "VarObjet" + }, + "parameters": [ + "Object", + "__ShakeObject_ShakeInProgress", + "=", + "0" + ] + } + ], + "actions": [ + { + "type": { + "value": "ModVarObjet" + }, + "parameters": [ + "Object", + "__ShakeObject_InitialShake", + "=", + "1" + ] + } + ] + }, + { + "type": "BuiltinCommonInstructions::Comment", + "color": { + "b": 109, + "g": 230, + "r": 255, + "textB": 0, + "textG": 0, + "textR": 0 + }, + "comment": "Initiate the onScenePostEvents function" + }, + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [], + "actions": [ + { + "type": { + "value": "ModVarObjet" + }, + "parameters": [ + "Object", + "__ShakeObject_ShakeInProgress", + "=", + "1" + ] + } + ] + } + ], + "parameters": [] + } + ], + "parameters": [ + { + "description": "Object", + "name": "Object", + "type": "object" + }, + { + "description": "Behavior", + "name": "Behavior", + "supplementaryInformation": "ShakeObject::ShakeObject_PositionAngle", + "type": "behavior" + }, + { + "description": "Duration of shake (in seconds) (Default: 0.5) ", + "name": "Duration", + "type": "expression" + }, + { + "description": "Amplitude of postion shake in X direction (in pixels) (For example: 5)", + "name": "PowerX", + "type": "expression" + }, + { + "description": "Amplitude of position shake in Y direction (in pixels) (For example: 5)", + "longDescription": "Use a negative number to make the single-shake move in the opposite direction.", + "name": "PowerY", + "type": "expression" + }, + { + "description": "Amplitude of angle rotation shake (in degrees) (For example: 5)", + "name": "PowerAngle", + "type": "expression" + }, + { + "description": "Amount of time between shakes (in seconds) (Default: 0.08)", + "longDescription": "For a single-shake effect, set it to the same value as \"Duration\".", + "name": "TimeBetweenShakes", + "type": "expression" + }, + { + "description": "Keep shaking until stopped", + "longDescription": "Duration value will be ignored", + "name": "ShakeForever", + "type": "yesorno" + } + ], + "objectGroups": [] + }, + { + "fullName": "", + "functionType": "Action", + "name": "doStepPostEvents", + "sentence": "", + "events": [ + { + "colorB": 228, + "colorG": 176, + "colorR": 74, + "creationTime": 0, + "name": "Shake Object", + "source": "", + "type": "BuiltinCommonInstructions::Group", + "events": [ + { + "colorB": 228, + "colorG": 176, + "colorR": 74, + "creationTime": 0, + "name": "Start shaking", + "source": "", + "type": "BuiltinCommonInstructions::Group", + "events": [ + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [ + { + "type": { + "value": "VarObjet" + }, + "parameters": [ + "Object", + "__ShakeObject_ShakeInProgress", + "=", + "1" + ] + } + ], + "actions": [], + "events": [ + { + "type": "BuiltinCommonInstructions::Comment", + "color": { + "b": 109, + "g": 230, + "r": 255, + "textB": 0, + "textG": 0, + "textR": 0 + }, + "comment": "Keep object shaking forever (if desired)" + }, + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [ + { + "type": { + "value": "ObjectVariableAsBoolean" + }, + "parameters": [ + "Object", + "__ShakeObject_ShakeForever", + "True" + ] + } + ], + "actions": [ + { + "type": { + "value": "ModVarObjet" + }, + "parameters": [ + "Object", + "__ShakeObject_Duration", + "=", + "100" + ] + }, + { + "type": { + "value": "ResetObjectTimer" + }, + "parameters": [ + "Object", + "\"__ShakeObject_DurationTimer\"" + ] + } + ] + }, + { + "colorB": 228, + "colorG": 176, + "colorR": 74, + "creationTime": 0, + "name": "Calculate movement of the shake", + "source": "", + "type": "BuiltinCommonInstructions::Group", + "events": [ + { + "type": "BuiltinCommonInstructions::Comment", + "color": { + "b": 109, + "g": 230, + "r": 255, + "textB": 0, + "textG": 0, + "textR": 0 + }, + "comment": "Run once before every shake movement" + }, + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [ + { + "type": { + "value": "BuiltinCommonInstructions::Or" + }, + "parameters": [], + "subInstructions": [ + { + "type": { + "value": "VarObjet" + }, + "parameters": [ + "Object", + "__ShakeObject_InitialShake", + "=", + "1" + ] + }, + { + "type": { + "value": "ObjectTimer" + }, + "parameters": [ + "Object", + "\"__ShakeObject_ShakeTimer\"", + "Object.Variable(__ShakeObject_TimeBetweenShakes)" + ] + } + ] + } + ], + "actions": [ + { + "type": { + "value": "ResetObjectTimer" + }, + "parameters": [ + "Object", + "\"__ShakeObject_ShakeTimer\"" + ] + }, + { + "type": { + "value": "ModVarObjet" + }, + "parameters": [ + "Object", + "__ShakeObject_ShakeCounter", + "+", + "1" + ] + } + ], + "events": [ + { + "colorB": 228, + "colorG": 176, + "colorR": 74, + "creationTime": 0, + "name": "Correct for drift and reset drift tracking variables", + "source": "", + "type": "BuiltinCommonInstructions::Group", + "events": [ + { + "type": "BuiltinCommonInstructions::Comment", + "color": { + "b": 109, + "g": 230, + "r": 255, + "textB": 0, + "textG": 0, + "textR": 0 + }, + "comment": "Move to correct any drift from previous shake" + }, + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [ + { + "type": { + "value": "BuiltinCommonInstructions::Or" + }, + "parameters": [], + "subInstructions": [ + { + "type": { + "value": "VarObjet" + }, + "parameters": [ + "Object", + "__ShakeObject_PowerX", + "!=", + "0" + ] + }, + { + "type": { + "value": "VarObjet" + }, + "parameters": [ + "Object", + "__ShakeObject_PowerY", + "!=", + "0" + ] + } + ] + } + ], + "actions": [ + { + "type": { + "value": "SetX" + }, + "parameters": [ + "Object", + "-", + "Object.Variable(__ShakeObject_DisplacementTravelledX)" + ] + }, + { + "type": { + "value": "SetY" + }, + "parameters": [ + "Object", + "-", + "Object.Variable(__ShakeObject_DisplacementTravelledY)" + ] + } + ] + }, + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [ + { + "type": { + "value": "VarObjet" + }, + "parameters": [ + "Object", + "__ShakeObject_PowerAngle", + "!=", + "0" + ] + } + ], + "actions": [ + { + "type": { + "value": "SetAngle" + }, + "parameters": [ + "Object", + "-", + "Object.Variable(__ShakeObject_AngleTravelled)" + ] + } + ] + }, + { + "type": "BuiltinCommonInstructions::Comment", + "color": { + "b": 109, + "g": 230, + "r": 255, + "textB": 0, + "textG": 0, + "textR": 0 + }, + "comment": "Reset drift detection variables" + }, + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [], + "actions": [ + { + "type": { + "value": "ModVarObjet" + }, + "parameters": [ + "Object", + "__ShakeObject_DisplacementTravelledX", + "=", + "0" + ] + }, + { + "type": { + "value": "ModVarObjet" + }, + "parameters": [ + "Object", + "__ShakeObject_DisplacementTravelledY", + "=", + "0" + ] + }, + { + "type": { + "value": "ModVarObjet" + }, + "parameters": [ + "Object", + "__ShakeObject_AngleTravelled", + "=", + "0" + ] + } + ] + } + ], + "parameters": [] + }, + { + "type": "BuiltinCommonInstructions::Comment", + "color": { + "b": 109, + "g": 230, + "r": 255, + "textB": 0, + "textG": 0, + "textR": 0 + }, + "comment": "Calculate Position Shake" + }, + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [ + { + "type": { + "value": "BuiltinCommonInstructions::Or" + }, + "parameters": [], + "subInstructions": [ + { + "type": { + "value": "VarObjet" + }, + "parameters": [ + "Object", + "__ShakeObject_PowerX", + "!=", + "0" + ] + }, + { + "type": { + "value": "VarObjet" + }, + "parameters": [ + "Object", + "__ShakeObject_PowerY", + "!=", + "0" + ] + } + ] + } + ], + "actions": [], + "events": [ + { + "type": "BuiltinCommonInstructions::Comment", + "color": { + "b": 109, + "g": 230, + "r": 255, + "textB": 0, + "textG": 0, + "textR": 0 + }, + "comment": "PositionDisplacement = (DesiredDuration - RunningTimer) / DesiredDuration * Amplitude * [-1 or 1]" + }, + { + "type": "BuiltinCommonInstructions::Comment", + "color": { + "b": 109, + "g": 230, + "r": 255, + "textB": 0, + "textG": 0, + "textR": 0 + }, + "comment": "Make initial shake NOT random so users can set a direction for a one-shake effect" + }, + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [ + { + "type": { + "value": "VarObjet" + }, + "parameters": [ + "Object", + "__ShakeObject_InitialShake", + "=", + "1" + ] + } + ], + "actions": [], + "events": [ + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [ + { + "type": { + "value": "VarObjet" + }, + "parameters": [ + "Object", + "__ShakeObject_PowerX", + "!=", + "0" + ] + } + ], + "actions": [ + { + "type": { + "value": "ModVarObjet" + }, + "parameters": [ + "Object", + "__ShakeObject_DisplacementX", + "=", + "(Object.Variable(__ShakeObject_Duration) - Object.ObjectTimerElapsedTime(\"__ShakeObject_DurationTimer\")) / Object.Variable(__ShakeObject_Duration) * Object.Variable(__ShakeObject_PowerX)" + ] + } + ] + }, + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [ + { + "type": { + "value": "VarObjet" + }, + "parameters": [ + "Object", + "__ShakeObject_PowerY", + "!=", + "0" + ] + } + ], + "actions": [ + { + "type": { + "value": "ModVarObjet" + }, + "parameters": [ + "Object", + "__ShakeObject_DisplacementY", + "=", + "(Object.Variable(__ShakeObject_Duration) - Object.ObjectTimerElapsedTime(\"__ShakeObject_DurationTimer\")) / Object.Variable(__ShakeObject_Duration) * Object.Variable(__ShakeObject_PowerY)" + ] + } + ] + } + ] + }, + { + "type": "BuiltinCommonInstructions::Comment", + "color": { + "b": 109, + "g": 230, + "r": 255, + "textB": 0, + "textG": 0, + "textR": 0 + }, + "comment": "After initial shake pick a random direction" + }, + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [ + { + "type": { + "value": "VarObjet" + }, + "parameters": [ + "Object", + "__ShakeObject_InitialShake", + "=", + "0" + ] + } + ], + "actions": [], + "events": [ + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [ + { + "type": { + "value": "VarObjet" + }, + "parameters": [ + "Object", + "__ShakeObject_PowerX", + "!=", + "0" + ] + } + ], + "actions": [ + { + "type": { + "value": "ModVarObjet" + }, + "parameters": [ + "Object", + "__ShakeObject_DisplacementX", + "=", + "(Object.Variable(__ShakeObject_Duration) - Object.ObjectTimerElapsedTime(\"__ShakeObject_DurationTimer\")) / Object.Variable(__ShakeObject_Duration) * Object.Variable(__ShakeObject_PowerX) * RandomWithStep(-1, 1, 2)" + ] + } + ] + }, + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [ + { + "type": { + "value": "VarObjet" + }, + "parameters": [ + "Object", + "__ShakeObject_PowerY", + "!=", + "0" + ] + } + ], + "actions": [ + { + "type": { + "value": "ModVarObjet" + }, + "parameters": [ + "Object", + "__ShakeObject_DisplacementY", + "=", + "(Object.Variable(__ShakeObject_Duration) - Object.ObjectTimerElapsedTime(\"__ShakeObject_DurationTimer\")) / Object.Variable(__ShakeObject_Duration) * Object.Variable(__ShakeObject_PowerY) * RandomWithStep(-1, 1, 2)" + ] + } + ] + } + ] + } + ] + }, + { + "type": "BuiltinCommonInstructions::Comment", + "color": { + "b": 109, + "g": 230, + "r": 255, + "textB": 0, + "textG": 0, + "textR": 0 + }, + "comment": "Calculate Rotation (angle) shake " + }, + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [ + { + "type": { + "value": "VarObjet" + }, + "parameters": [ + "Object", + "__ShakeObject_PowerAngle", + "!=", + "0" + ] + } + ], + "actions": [], + "events": [ + { + "type": "BuiltinCommonInstructions::Comment", + "color": { + "b": 109, + "g": 230, + "r": 255, + "textB": 0, + "textG": 0, + "textR": 0 + }, + "comment": "Every \"even\" shake, rotate clockwise" + }, + { + "type": "BuiltinCommonInstructions::Comment", + "color": { + "b": 109, + "g": 230, + "r": 255, + "textB": 0, + "textG": 0, + "textR": 0 + }, + "comment": "AngleDisplacement = (DesiredDuration - RunningTimer) / DesiredDuration * Amplitude" + }, + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [ + { + "type": { + "value": "BuiltinCommonInstructions::CompareNumbers" + }, + "parameters": [ + "mod(Object.Variable(__ShakeObject_ShakeCounter),2)", + "=", + "0" + ] + } + ], + "actions": [ + { + "type": { + "value": "ModVarObjet" + }, + "parameters": [ + "Object", + "__ShakeObject_DisplacementAngle", + "=", + "(Object.Variable(__ShakeObject_Duration) - Object.ObjectTimerElapsedTime(\"__ShakeObject_DurationTimer\")) / Object.Variable(__ShakeObject_Duration) * Object.Variable(__ShakeObject_PowerAngle)" + ] + } + ] + }, + { + "type": "BuiltinCommonInstructions::Comment", + "color": { + "b": 109, + "g": 230, + "r": 255, + "textB": 0, + "textG": 0, + "textR": 0 + }, + "comment": "Every \"odd\" shake, rotate counter-clockwise" + }, + { + "type": "BuiltinCommonInstructions::Comment", + "color": { + "b": 109, + "g": 230, + "r": 255, + "textB": 0, + "textG": 0, + "textR": 0 + }, + "comment": "AngleDisplacement = (DesiredDuration - RunningTimer) / DesiredDuration * Amplitude" + }, + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [ + { + "type": { + "value": "BuiltinCommonInstructions::CompareNumbers" + }, + "parameters": [ + "mod(Object.Variable(__ShakeObject_ShakeCounter),2)", + "=", + "1" + ] + } + ], + "actions": [ + { + "type": { + "value": "ModVarObjet" + }, + "parameters": [ + "Object", + "__ShakeObject_DisplacementAngle", + "=", + "-1 *(Object.Variable(__ShakeObject_Duration) - Object.ObjectTimerElapsedTime(\"__ShakeObject_DurationTimer\")) / Object.Variable(__ShakeObject_Duration) * Object.Variable(__ShakeObject_PowerAngle)" + ] + } + ] + } + ] + }, + { + "type": "BuiltinCommonInstructions::Comment", + "color": { + "b": 109, + "g": 230, + "r": 255, + "textB": 0, + "textG": 0, + "textR": 0 + }, + "comment": "Save that initial shake has been processed" + }, + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [], + "actions": [ + { + "type": { + "value": "ModVarObjet" + }, + "parameters": [ + "Object", + "__ShakeObject_InitialShake", + "=", + "0" + ] + } + ] + } + ] + } + ], + "parameters": [] + }, + { + "colorB": 228, + "colorG": 176, + "colorR": 74, + "creationTime": 0, + "name": "Move object", + "source": "", + "type": "BuiltinCommonInstructions::Group", + "events": [ + { + "type": "BuiltinCommonInstructions::Comment", + "color": { + "b": 109, + "g": 230, + "r": 255, + "textB": 0, + "textG": 0, + "textR": 0 + }, + "comment": "Calculate the fraction of shake that occured during this frame" + }, + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [], + "actions": [ + { + "type": { + "value": "ModVarObjet" + }, + "parameters": [ + "Object", + "__ShakeObject_PercentTimeElapsedThisFrame", + "=", + "min(1,TimeDelta()/Object.Variable(__ShakeObject_TimeBetweenShakes))" + ] + } + ] + }, + { + "type": "BuiltinCommonInstructions::Comment", + "color": { + "b": 109, + "g": 230, + "r": 255, + "textB": 0, + "textG": 0, + "textR": 0 + }, + "comment": "Increase change for the first half of the shake (move away from original values)" + }, + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [ + { + "type": { + "inverted": true, + "value": "ObjectTimer" + }, + "parameters": [ + "Object", + "\"__ShakeObject_ShakeTimer\"", + "Object.Variable(__ShakeObject_TimeBetweenShakes)/2" + ] + } + ], + "actions": [], + "events": [ + { + "type": "BuiltinCommonInstructions::Comment", + "color": { + "b": 109, + "g": 230, + "r": 255, + "textB": 0, + "textG": 0, + "textR": 0 + }, + "comment": "Change position" + }, + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [ + { + "type": { + "value": "BuiltinCommonInstructions::Or" + }, + "parameters": [], + "subInstructions": [ + { + "type": { + "value": "VarObjet" + }, + "parameters": [ + "Object", + "__ShakeObject_PowerX", + "!=", + "0" + ] + }, + { + "type": { + "value": "VarObjet" + }, + "parameters": [ + "Object", + "__ShakeObject_PowerY", + "!=", + "0" + ] + } + ] + } + ], + "actions": [ + { + "type": { + "value": "SetX" + }, + "parameters": [ + "Object", + "+", + "round(1024 * Object.Variable(__ShakeObject_DisplacementX) * Object.Variable(__ShakeObject_PercentTimeElapsedThisFrame)) / 1024" + ] + }, + { + "type": { + "value": "SetY" + }, + "parameters": [ + "Object", + "+", + "round(1024 * Object.Variable(__ShakeObject_DisplacementY) * Object.Variable(__ShakeObject_PercentTimeElapsedThisFrame)) / 1024" + ] + } + ], + "events": [ + { + "type": "BuiltinCommonInstructions::Comment", + "color": { + "b": 109, + "g": 230, + "r": 255, + "textB": 0, + "textG": 0, + "textR": 0 + }, + "comment": "Save movement to calculate drift" + }, + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [], + "actions": [ + { + "type": { + "value": "ModVarObjet" + }, + "parameters": [ + "Object", + "__ShakeObject_DisplacementTravelledX", + "+", + "round(1024 * Object.Variable(__ShakeObject_DisplacementX) * Object.Variable(__ShakeObject_PercentTimeElapsedThisFrame)) / 1024" + ] + }, + { + "type": { + "value": "ModVarObjet" + }, + "parameters": [ + "Object", + "__ShakeObject_DisplacementTravelledY", + "+", + "round(1024 * Object.Variable(__ShakeObject_DisplacementY) * Object.Variable(__ShakeObject_PercentTimeElapsedThisFrame)) / 1024" + ] + } + ] + } + ] + }, + { + "type": "BuiltinCommonInstructions::Comment", + "color": { + "b": 109, + "g": 230, + "r": 255, + "textB": 0, + "textG": 0, + "textR": 0 + }, + "comment": "Change angle" + }, + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [ + { + "type": { + "value": "BuiltinCommonInstructions::CompareNumbers" + }, + "parameters": [ + "Object.Variable(__ShakeObject_PowerAngle)", + "!=", + "0" + ] + } + ], + "actions": [ + { + "type": { + "value": "SetAngle" + }, + "parameters": [ + "Object", + "+", + "round(1024 * Object.Variable(__ShakeObject_DisplacementAngle) * Object.Variable(__ShakeObject_PercentTimeElapsedThisFrame)) / 1024" + ] + } + ], + "events": [ + { + "type": "BuiltinCommonInstructions::Comment", + "color": { + "b": 109, + "g": 230, + "r": 255, + "textB": 0, + "textG": 0, + "textR": 0 + }, + "comment": "Save movement to calculate drift" + }, + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [], + "actions": [ + { + "type": { + "value": "ModVarObjet" + }, + "parameters": [ + "Object", + "__ShakeObject_AngleTravelled", + "+", + "round(1024 * Object.Variable(__ShakeObject_DisplacementAngle) * Object.Variable(__ShakeObject_PercentTimeElapsedThisFrame)) / 1024" + ] + } + ] + } + ] + } + ] + }, + { + "type": "BuiltinCommonInstructions::Comment", + "color": { + "b": 109, + "g": 230, + "r": 255, + "textB": 0, + "textG": 0, + "textR": 0 + }, + "comment": "Decrease change the second half of the shake (return to original position)" + }, + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [ + { + "type": { + "value": "ObjectTimer" + }, + "parameters": [ + "Object", + "\"__ShakeObject_ShakeTimer\"", + "Object.Variable(__ShakeObject_TimeBetweenShakes)/2" + ] + } + ], + "actions": [], + "events": [ + { + "type": "BuiltinCommonInstructions::Comment", + "color": { + "b": 109, + "g": 230, + "r": 255, + "textB": 0, + "textG": 0, + "textR": 0 + }, + "comment": "Change position" + }, + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [ + { + "type": { + "value": "BuiltinCommonInstructions::Or" + }, + "parameters": [], + "subInstructions": [ + { + "type": { + "value": "VarObjet" + }, + "parameters": [ + "Object", + "__ShakeObject_PowerX", + "!=", + "0" + ] + }, + { + "type": { + "value": "VarObjet" + }, + "parameters": [ + "Object", + "__ShakeObject_PowerY", + "!=", + "0" + ] + } + ] + } + ], + "actions": [ + { + "type": { + "value": "SetX" + }, + "parameters": [ + "Object", + "-", + "round(1024 * Object.Variable(__ShakeObject_DisplacementX) * Object.Variable(__ShakeObject_PercentTimeElapsedThisFrame)) / 1024" + ] + }, + { + "type": { + "value": "SetY" + }, + "parameters": [ + "Object", + "-", + "round(1024 * Object.Variable(__ShakeObject_DisplacementY) * Object.Variable(__ShakeObject_PercentTimeElapsedThisFrame)) / 1024" + ] + } + ], + "events": [ + { + "type": "BuiltinCommonInstructions::Comment", + "color": { + "b": 109, + "g": 230, + "r": 255, + "textB": 0, + "textG": 0, + "textR": 0 + }, + "comment": "Save movement to calculate drift" + }, + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [], + "actions": [ + { + "type": { + "value": "ModVarObjet" + }, + "parameters": [ + "Object", + "__ShakeObject_DisplacementTravelledX", + "-", + "round(1024 * Object.Variable(__ShakeObject_DisplacementX) * Object.Variable(__ShakeObject_PercentTimeElapsedThisFrame)) / 1024" + ] + }, + { + "type": { + "value": "ModVarObjet" + }, + "parameters": [ + "Object", + "__ShakeObject_DisplacementTravelledY", + "-", + "round(1024 * Object.Variable(__ShakeObject_DisplacementY) * Object.Variable(__ShakeObject_PercentTimeElapsedThisFrame)) / 1024" + ] + } + ] + } + ] + }, + { + "type": "BuiltinCommonInstructions::Comment", + "color": { + "b": 109, + "g": 230, + "r": 255, + "textB": 0, + "textG": 0, + "textR": 0 + }, + "comment": "Change angle" + }, + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [ + { + "type": { + "value": "BuiltinCommonInstructions::CompareNumbers" + }, + "parameters": [ + "Object.Variable(__ShakeObject_PowerAngle)", + "!=", + "0" + ] + } + ], + "actions": [ + { + "type": { + "value": "SetAngle" + }, + "parameters": [ + "Object", + "-", + "round(1024 * Object.Variable(__ShakeObject_DisplacementAngle) * Object.Variable(__ShakeObject_PercentTimeElapsedThisFrame)) / 1024" + ] + } + ], + "events": [ + { + "type": "BuiltinCommonInstructions::Comment", + "color": { + "b": 109, + "g": 230, + "r": 255, + "textB": 0, + "textG": 0, + "textR": 0 + }, + "comment": "Save movement to calculate drift" + }, + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [], + "actions": [ + { + "type": { + "value": "ModVarObjet" + }, + "parameters": [ + "Object", + "__ShakeObject_AngleTravelled", + "-", + "round(1024 * Object.Variable(__ShakeObject_DisplacementAngle) * Object.Variable(__ShakeObject_PercentTimeElapsedThisFrame)) / 1024" + ] + } + ] + } + ] + } + ] + } + ], + "parameters": [] + } + ] + } + ], + "parameters": [] + }, + { + "colorB": 228, + "colorG": 176, + "colorR": 74, + "creationTime": 0, + "name": "Stop shaking", + "source": "", + "type": "BuiltinCommonInstructions::Group", + "events": [ + { + "type": "BuiltinCommonInstructions::Comment", + "color": { + "b": 109, + "g": 230, + "r": 255, + "textB": 0, + "textG": 0, + "textR": 0 + }, + "comment": "Stop shaking when the duration has been reached (or if the stop shaking function was called)" + }, + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [ + { + "type": { + "value": "BuiltinCommonInstructions::Or" + }, + "parameters": [], + "subInstructions": [ + { + "type": { + "value": "ObjectTimer" + }, + "parameters": [ + "Object", + "\"__ShakeObject_DurationTimer\"", + "Object.Variable(__ShakeObject_Duration)" + ] + }, + { + "type": { + "value": "VarObjet" + }, + "parameters": [ + "Object", + "__ShakeObject_ShakeInProgress", + "=", + "-1" + ] + } + ] + }, + { + "type": { + "value": "BuiltinCommonInstructions::Once" + }, + "parameters": [] + } + ], + "actions": [ + { + "type": { + "value": "ModVarObjet" + }, + "parameters": [ + "Object", + "__ShakeObject_ShakeInProgress", + "=", + "0" + ] + }, + { + "type": { + "value": "ModVarObjet" + }, + "parameters": [ + "Object", + "__ShakeObject_ShakeCounter", + "=", + "0" + ] + } + ], + "events": [ + { + "colorB": 228, + "colorG": 176, + "colorR": 74, + "creationTime": 0, + "name": "Correct for drift and reset drift tracking variables", + "source": "", + "type": "BuiltinCommonInstructions::Group", + "events": [ + { + "type": "BuiltinCommonInstructions::Comment", + "color": { + "b": 109, + "g": 230, + "r": 255, + "textB": 0, + "textG": 0, + "textR": 0 + }, + "comment": "Move to correct any drift from previous shake" + }, + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [ + { + "type": { + "value": "BuiltinCommonInstructions::Or" + }, + "parameters": [], + "subInstructions": [ + { + "type": { + "value": "VarObjet" + }, + "parameters": [ + "Object", + "__ShakeObject_PowerX", + "!=", + "0" + ] + }, + { + "type": { + "value": "VarObjet" + }, + "parameters": [ + "Object", + "__ShakeObject_PowerY", + "!=", + "0" + ] + } + ] + } + ], + "actions": [ + { + "type": { + "value": "SetX" + }, + "parameters": [ + "Object", + "-", + "Object.Variable(__ShakeObject_DisplacementTravelledX)" + ] + }, + { + "type": { + "value": "SetY" + }, + "parameters": [ + "Object", + "-", + "Object.Variable(__ShakeObject_DisplacementTravelledY)" + ] + } + ] + }, + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [ + { + "type": { + "value": "VarObjet" + }, + "parameters": [ + "Object", + "__ShakeObject_PowerAngle", + "!=", + "0" + ] + } + ], + "actions": [ + { + "type": { + "value": "SetAngle" + }, + "parameters": [ + "Object", + "-", + "Object.Variable(__ShakeObject_AngleTravelled)" + ] + } + ] + }, + { + "type": "BuiltinCommonInstructions::Comment", + "color": { + "b": 109, + "g": 230, + "r": 255, + "textB": 0, + "textG": 0, + "textR": 0 + }, + "comment": "Reset drift detection variables" + }, + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [], + "actions": [ + { + "type": { + "value": "ModVarObjet" + }, + "parameters": [ + "Object", + "__ShakeObject_DisplacementTravelledX", + "=", + "0" + ] + }, + { + "type": { + "value": "ModVarObjet" + }, + "parameters": [ + "Object", + "__ShakeObject_DisplacementTravelledY", + "=", + "0" + ] + }, + { + "type": { + "value": "ModVarObjet" + }, + "parameters": [ + "Object", + "__ShakeObject_AngleTravelled", + "=", + "0" + ] + } + ] + } + ], + "parameters": [] + } + ] + } + ], + "parameters": [] + } + ], + "parameters": [] + } + ], + "parameters": [ + { + "description": "Object", + "name": "Object", + "type": "object" + }, + { + "description": "Behavior", + "name": "Behavior", + "supplementaryInformation": "ShakeObject::ShakeObject_PositionAngle", + "type": "behavior" + } + ], + "objectGroups": [] + }, + { + "description": "Stop any shaking of object that was initiated by the Shake Object extension.", + "fullName": "Stop shaking the object", + "functionType": "Action", + "name": "StopShaking", + "sentence": "Stop shaking _PARAM0_", + "events": [ + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [ + { + "type": { + "value": "ShakeObject::ShakeObject_PositionAngle::IsShaking" + }, + "parameters": [ + "Object", + "Behavior", + "" + ] + } + ], + "actions": [ + { + "type": { + "value": "ModVarObjet" + }, + "parameters": [ + "Object", + "__ShakeObject_ShakeInProgress", + "=", + "-1" + ] + } + ] + } + ], + "parameters": [ + { + "description": "Object", + "name": "Object", + "type": "object" + }, + { + "description": "Behavior", + "name": "Behavior", + "supplementaryInformation": "ShakeObject::ShakeObject_PositionAngle", + "type": "behavior" + } + ], + "objectGroups": [] + }, + { + "description": "Check if an object is shaking.", + "fullName": "Check if an object is shaking", + "functionType": "Condition", + "name": "IsShaking", + "sentence": "_PARAM0_ is shaking", + "events": [ + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [ + { + "type": { + "value": "VarObjet" + }, + "parameters": [ + "Object", + "__ShakeObject_ShakeInProgress", + "=", + "1" + ] + } + ], + "actions": [ + { + "type": { + "value": "SetReturnBoolean" + }, + "parameters": [ + "True" + ] + } + ] + } + ], + "parameters": [ + { + "description": "Object", + "name": "Object", + "type": "object" + }, + { + "description": "Behavior", + "name": "Behavior", + "supplementaryInformation": "ShakeObject::ShakeObject_PositionAngle", + "type": "behavior" + } + ], + "objectGroups": [] + } + ], + "eventsFunctionsFolderStructure": { + "folderName": "__ROOT", + "children": [ + { + "functionName": "ShakeObject_PositionAngle" + }, + { + "functionName": "doStepPostEvents" + }, + { + "functionName": "StopShaking" + }, + { + "functionName": "IsShaking" + } + ] + }, + "propertyDescriptors": [], + "propertiesFolderStructure": { + "folderName": "__ROOT" + } + }, + { + "description": "Shake objects with translation, rotation and scale.", + "fullName": "Shake object (position, angle, scale)", + "helpPath": "", + "iconUrl": "", + "name": "ShakeObject_PositionAngleScale", + "objectType": "", + "previewIconUrl": "", + "eventsFunctions": [ + { + "description": "Shake an object, using one or more ways to shake (position, angle, scale). Make sure to \"Stop shaking\" before starting a new shake if it uses different parameters.", + "fullName": "Shake object (position, angle, scale)", + "functionType": "Action", + "name": "ShakeObject_PositionAngleScale", + "sentence": "Shake object _PARAM0_ for _PARAM2_ seconds. Modify position amplitude _PARAM3_ on X axis and _PARAM4_ on Y axis, angle rotation amplitude _PARAM5_, and scale amplitude _PARAM6_. Wait _PARAM7_ seconds between shakes. Keep shaking until stopped: _PARAM8_", + "events": [ + { + "colorB": 228, + "colorG": 176, + "colorR": 74, + "creationTime": 0, + "name": "Shake Object", + "source": "", + "type": "BuiltinCommonInstructions::Group", + "events": [ + { + "type": "BuiltinCommonInstructions::Comment", + "color": { + "b": 109, + "g": 230, + "r": 255, + "textB": 0, + "textG": 0, + "textR": 0 + }, + "comment": "Start/Reset duration timer" + }, + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [], + "actions": [ + { + "type": { + "value": "ResetObjectTimer" + }, + "parameters": [ + "Object", + "\"__ShakeObject_DurationTimer\"" + ] + } + ] + }, + { + "type": "BuiltinCommonInstructions::Comment", + "color": { + "b": 109, + "g": 230, + "r": 255, + "textB": 0, + "textG": 0, + "textR": 0 + }, + "comment": "Pass input parameters to global variables so that onScenePostEvents can use them" + }, + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [], + "actions": [ + { + "type": { + "value": "ModVarObjet" + }, + "parameters": [ + "Object", + "__ShakeObject_Duration", + "=", + "Duration" + ] + }, + { + "type": { + "value": "ModVarObjet" + }, + "parameters": [ + "Object", + "__ShakeObject_PowerX", + "=", + "PowerX" + ] + }, + { + "type": { + "value": "ModVarObjet" + }, + "parameters": [ + "Object", + "__ShakeObject_PowerY", + "=", + "PowerY" + ] + }, + { + "type": { + "value": "ModVarObjet" + }, + "parameters": [ + "Object", + "__ShakeObject_PowerAngle", + "=", + "PowerAngle" + ] + }, + { + "type": { + "value": "ModVarObjet" + }, + "parameters": [ + "Object", + "__ShakeObject_PowerScale", + "=", + "PowerScale" + ] + }, + { + "type": { + "value": "ModVarObjet" + }, + "parameters": [ + "Object", + "__ShakeObject_TimeBetweenShakes", + "=", + "TimeBetweenShakes" + ] + } + ] + }, + { + "type": "BuiltinCommonInstructions::Comment", + "color": { + "b": 109, + "g": 230, + "r": 255, + "textB": 0, + "textG": 0, + "textR": 0 + }, + "comment": "Determine if the shake should keep going until stopped" + }, + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [], + "actions": [ + { + "type": { + "value": "SetObjectVariableAsBoolean" + }, + "parameters": [ + "Object", + "__ShakeObject_ShakeForever", + "False" + ] + } + ] + }, + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [ + { + "type": { + "value": "BooleanVariable" + }, + "parameters": [ + "ShakeForever", + "True", + "" + ] + } + ], + "actions": [ + { + "type": { + "value": "SetObjectVariableAsBoolean" + }, + "parameters": [ + "Object", + "__ShakeObject_ShakeForever", + "True" + ] + } + ] + }, + { + "type": "BuiltinCommonInstructions::Comment", + "color": { + "b": 109, + "g": 230, + "r": 255, + "textB": 0, + "textG": 0, + "textR": 0 + }, + "comment": "Add default values if none were provided" + }, + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [ + { + "type": { + "value": "VarObjet" + }, + "parameters": [ + "Object", + "__ShakeObject_Duration", + "=", + "0" + ] + } + ], + "actions": [ + { + "type": { + "value": "ModVarObjet" + }, + "parameters": [ + "Object", + "__ShakeObject_Duration", + "=", + "0.5" + ] + } + ] + }, + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [ + { + "type": { + "value": "VarObjet" + }, + "parameters": [ + "Object", + "__ShakeObject_TimeBetweenShakes", + "=", + "0" + ] + } + ], + "actions": [ + { + "type": { + "value": "ModVarObjet" + }, + "parameters": [ + "Object", + "__ShakeObject_TimeBetweenShakes", + "=", + "0.08" + ] + } + ] + }, + { + "type": "BuiltinCommonInstructions::Comment", + "color": { + "b": 109, + "g": 230, + "r": 255, + "textB": 0, + "textG": 0, + "textR": 0 + }, + "comment": "If duration is less than a single shake, increase duration to make 1 full shake" + }, + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [ + { + "type": { + "value": "VarObjet" + }, + "parameters": [ + "Object", + "__ShakeObject_Duration", + "<", + "Object.Variable(__ShakeObject_TimeBetweenShakes)" + ] + } + ], + "actions": [ + { + "type": { + "value": "ModVarObjet" + }, + "parameters": [ + "Object", + "__ShakeObject_Duration", + "=", + "Object.Variable(__ShakeObject_TimeBetweenShakes)" + ] + } + ] + }, + { + "type": "BuiltinCommonInstructions::Comment", + "color": { + "b": 109, + "g": 230, + "r": 255, + "textB": 0, + "textG": 0, + "textR": 0 + }, + "comment": "Detect initial shake" + }, + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [ + { + "type": { + "value": "VarObjet" + }, + "parameters": [ + "Object", + "__ShakeObject_ShakeInProgress", + "=", + "0" + ] + } + ], + "actions": [ + { + "type": { + "value": "ModVarObjet" + }, + "parameters": [ + "Object", + "__ShakeObject_InitialShake", + "=", + "1" + ] + } + ] + }, + { + "type": "BuiltinCommonInstructions::Comment", + "color": { + "b": 109, + "g": 230, + "r": 255, + "textB": 0, + "textG": 0, + "textR": 0 + }, + "comment": "Initiate the onScenePostEvents function" + }, + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [], + "actions": [ + { + "type": { + "value": "ModVarObjet" + }, + "parameters": [ + "Object", + "__ShakeObject_ShakeInProgress", + "=", + "1" + ] + } + ] + } + ], + "parameters": [] + } + ], + "parameters": [ + { + "description": "Object", + "name": "Object", + "type": "object" + }, + { + "description": "Behavior", + "name": "Behavior", + "supplementaryInformation": "ShakeObject::ShakeObject_PositionAngleScale", + "type": "behavior" + }, + { + "description": "Duration of shake (in seconds) (Default: 0.5)", + "name": "Duration", + "type": "expression" + }, + { + "description": "Amplitude of postion shake in X direction (in pixels) (For example: 5)", + "name": "PowerX", + "type": "expression" + }, + { + "description": "Amplitude of position shake in Y direction (in pixels) (For example: 5)", + "longDescription": "Use a negative number to make the single-shake move in the opposite direction.", + "name": "PowerY", + "type": "expression" + }, + { + "description": "Amplitude of angle rotation shake (in degrees) (For example: 5)", + "name": "PowerAngle", + "type": "expression" + }, + { + "description": "Amplitude of scale shake (in percent change) (For example: 5)", + "name": "PowerScale", + "type": "expression" + }, + { + "description": "Amount of time between shakes (in seconds) (Default: 0.08)", + "longDescription": "For a single-shake effect, set it to the same value as \"Duration\".", + "name": "TimeBetweenShakes", + "type": "expression" + }, + { + "description": "Keep shaking until stopped", + "longDescription": "Duration value will be ignored", + "name": "ShakeForever", + "type": "yesorno" + } + ], + "objectGroups": [] + }, + { + "fullName": "", + "functionType": "Action", + "name": "doStepPostEvents", + "sentence": "", + "events": [ + { + "colorB": 228, + "colorG": 176, + "colorR": 74, + "creationTime": 0, + "name": "Shake Object", + "source": "", + "type": "BuiltinCommonInstructions::Group", + "events": [ + { + "colorB": 228, + "colorG": 176, + "colorR": 74, + "creationTime": 0, + "name": "Start shaking", + "source": "", + "type": "BuiltinCommonInstructions::Group", + "events": [ + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [ + { + "type": { + "value": "VarObjet" + }, + "parameters": [ + "Object", + "__ShakeObject_ShakeInProgress", + "=", + "1" + ] + } + ], + "actions": [], + "events": [ + { + "type": "BuiltinCommonInstructions::Comment", + "color": { + "b": 109, + "g": 230, + "r": 255, + "textB": 0, + "textG": 0, + "textR": 0 + }, + "comment": "Keep object shaking forever (if desired)" + }, + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [ + { + "type": { + "value": "ObjectVariableAsBoolean" + }, + "parameters": [ + "Object", + "__ShakeObject_ShakeForever", + "True" + ] + } + ], + "actions": [ + { + "type": { + "value": "ModVarObjet" + }, + "parameters": [ + "Object", + "__ShakeObject_Duration", + "=", + "100" + ] + }, + { + "type": { + "value": "ResetObjectTimer" + }, + "parameters": [ + "Object", + "\"__ShakeObject_DurationTimer\"" + ] + } + ] + }, + { + "colorB": 228, + "colorG": 176, + "colorR": 74, + "creationTime": 0, + "name": "Calculate movement of the shake", + "source": "", + "type": "BuiltinCommonInstructions::Group", + "events": [ + { + "type": "BuiltinCommonInstructions::Comment", + "color": { + "b": 109, + "g": 230, + "r": 255, + "textB": 0, + "textG": 0, + "textR": 0 + }, + "comment": "Run once before every shake movement" + }, + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [ + { + "type": { + "value": "BuiltinCommonInstructions::Or" + }, + "parameters": [], + "subInstructions": [ + { + "type": { + "value": "VarObjet" + }, + "parameters": [ + "Object", + "__ShakeObject_InitialShake", + "=", + "1" + ] + }, + { + "type": { + "value": "ObjectTimer" + }, + "parameters": [ + "Object", + "\"__ShakeObject_ShakeTimer\"", + "Object.Variable(__ShakeObject_TimeBetweenShakes)" + ] + } + ] + } + ], + "actions": [ + { + "type": { + "value": "ResetObjectTimer" + }, + "parameters": [ + "Object", + "\"__ShakeObject_ShakeTimer\"" + ] + }, + { + "type": { + "value": "ModVarObjet" + }, + "parameters": [ + "Object", + "__ShakeObject_ShakeCounter", + "+", + "1" + ] + } + ], + "events": [ + { + "colorB": 228, + "colorG": 176, + "colorR": 74, + "creationTime": 0, + "folded": true, + "name": "Correct for drift and reset drift tracking variables", + "source": "", + "type": "BuiltinCommonInstructions::Group", + "events": [ + { + "type": "BuiltinCommonInstructions::Comment", + "color": { + "b": 109, + "g": 230, + "r": 255, + "textB": 0, + "textG": 0, + "textR": 0 + }, + "comment": "Move to correct any drift from previous shake" + }, + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [ + { + "type": { + "value": "BuiltinCommonInstructions::Or" + }, + "parameters": [], + "subInstructions": [ + { + "type": { + "value": "VarObjet" + }, + "parameters": [ + "Object", + "__ShakeObject_PowerX", + "!=", + "0" + ] + }, + { + "type": { + "value": "VarObjet" + }, + "parameters": [ + "Object", + "__ShakeObject_PowerY", + "!=", + "0" + ] + } + ] + } + ], + "actions": [ + { + "type": { + "value": "SetX" + }, + "parameters": [ + "Object", + "-", + "Object.Variable(__ShakeObject_DisplacementTravelledX)" + ] + }, + { + "type": { + "value": "SetY" + }, + "parameters": [ + "Object", + "-", + "Object.Variable(__ShakeObject_DisplacementTravelledY)" + ] + } + ] + }, + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [ + { + "type": { + "value": "VarObjet" + }, + "parameters": [ + "Object", + "__ShakeObject_PowerAngle", + "!=", + "0" + ] + } + ], + "actions": [ + { + "type": { + "value": "SetAngle" + }, + "parameters": [ + "Object", + "-", + "Object.Variable(__ShakeObject_AngleTravelled)" + ] + } + ] + }, + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [ + { + "type": { + "value": "VarObjet" + }, + "parameters": [ + "Object", + "__ShakeObject_PowerScale", + "!=", + "0" + ] + } + ], + "actions": [ + { + "type": { + "value": "ScalableCapability::ScalableBehavior::SetValue" + }, + "parameters": [ + "Object", + "Scale", + "-", + "Object.Variable(__ShakeObject_ScaleTravelled)" + ] + } + ] + }, + { + "type": "BuiltinCommonInstructions::Comment", + "color": { + "b": 109, + "g": 230, + "r": 255, + "textB": 0, + "textG": 0, + "textR": 0 + }, + "comment": "Reset drift detection variables" + }, + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [], + "actions": [ + { + "type": { + "value": "ModVarObjet" + }, + "parameters": [ + "Object", + "__ShakeObject_DisplacementTravelledX", + "=", + "0" + ] + }, + { + "type": { + "value": "ModVarObjet" + }, + "parameters": [ + "Object", + "__ShakeObject_DisplacementTravelledY", + "=", + "0" + ] + }, + { + "type": { + "value": "ModVarObjet" + }, + "parameters": [ + "Object", + "__ShakeObject_AngleTravelled", + "=", + "0" + ] + }, + { + "type": { + "value": "ModVarObjet" + }, + "parameters": [ + "Object", + "__ShakeObject_ScaleTravelled", + "=", + "0" + ] + } + ] + } + ], + "parameters": [] + }, + { + "type": "BuiltinCommonInstructions::Comment", + "color": { + "b": 109, + "g": 230, + "r": 255, + "textB": 0, + "textG": 0, + "textR": 0 + }, + "comment": "Calculate Position Shake" + }, + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [ + { + "type": { + "value": "BuiltinCommonInstructions::Or" + }, + "parameters": [], + "subInstructions": [ + { + "type": { + "value": "VarObjet" + }, + "parameters": [ + "Object", + "__ShakeObject_PowerX", + "!=", + "0" + ] + }, + { + "type": { + "value": "VarObjet" + }, + "parameters": [ + "Object", + "__ShakeObject_PowerY", + "!=", + "0" + ] + } + ] + } + ], + "actions": [], + "events": [ + { + "type": "BuiltinCommonInstructions::Comment", + "color": { + "b": 109, + "g": 230, + "r": 255, + "textB": 0, + "textG": 0, + "textR": 0 + }, + "comment": "PositionDisplacement = (DesiredDuration - RunningTimer) / DesiredDuration * Amplitude * [-1 or 1]" + }, + { + "type": "BuiltinCommonInstructions::Comment", + "color": { + "b": 109, + "g": 230, + "r": 255, + "textB": 0, + "textG": 0, + "textR": 0 + }, + "comment": "Make initial shake NOT random so users can set a direction for a one-shake effect" + }, + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [ + { + "type": { + "value": "VarObjet" + }, + "parameters": [ + "Object", + "__ShakeObject_InitialShake", + "=", + "1" + ] + } + ], + "actions": [], + "events": [ + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [ + { + "type": { + "value": "VarObjet" + }, + "parameters": [ + "Object", + "__ShakeObject_PowerX", + "!=", + "0" + ] + } + ], + "actions": [ + { + "type": { + "value": "ModVarObjet" + }, + "parameters": [ + "Object", + "__ShakeObject_DisplacementX", + "=", + "(Object.Variable(__ShakeObject_Duration) - Object.ObjectTimerElapsedTime(\"__ShakeObject_DurationTimer\")) / Object.Variable(__ShakeObject_Duration) * Object.Variable(__ShakeObject_PowerX)" + ] + } + ] + }, + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [ + { + "type": { + "value": "VarObjet" + }, + "parameters": [ + "Object", + "__ShakeObject_PowerY", + "!=", + "0" + ] + } + ], + "actions": [ + { + "type": { + "value": "ModVarObjet" + }, + "parameters": [ + "Object", + "__ShakeObject_DisplacementY", + "=", + "(Object.Variable(__ShakeObject_Duration) - Object.ObjectTimerElapsedTime(\"__ShakeObject_DurationTimer\")) / Object.Variable(__ShakeObject_Duration) * Object.Variable(__ShakeObject_PowerY)" + ] + } + ] + } + ] + }, + { + "type": "BuiltinCommonInstructions::Comment", + "color": { + "b": 109, + "g": 230, + "r": 255, + "textB": 0, + "textG": 0, + "textR": 0 + }, + "comment": "After initial shake pick a random direction" + }, + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [ + { + "type": { + "value": "VarObjet" + }, + "parameters": [ + "Object", + "__ShakeObject_InitialShake", + "=", + "0" + ] + } + ], + "actions": [], + "events": [ + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [ + { + "type": { + "value": "VarObjet" + }, + "parameters": [ + "Object", + "__ShakeObject_PowerX", + "!=", + "0" + ] + } + ], + "actions": [ + { + "type": { + "value": "ModVarObjet" + }, + "parameters": [ + "Object", + "__ShakeObject_DisplacementX", + "=", + "(Object.Variable(__ShakeObject_Duration) - Object.ObjectTimerElapsedTime(\"__ShakeObject_DurationTimer\")) / Object.Variable(__ShakeObject_Duration) * Object.Variable(__ShakeObject_PowerX) * RandomWithStep(-1, 1, 2)" + ] + } + ] + }, + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [ + { + "type": { + "value": "VarObjet" + }, + "parameters": [ + "Object", + "__ShakeObject_PowerY", + "!=", + "0" + ] + } + ], + "actions": [ + { + "type": { + "value": "ModVarObjet" + }, + "parameters": [ + "Object", + "__ShakeObject_DisplacementY", + "=", + "(Object.Variable(__ShakeObject_Duration) - Object.ObjectTimerElapsedTime(\"__ShakeObject_DurationTimer\")) / Object.Variable(__ShakeObject_Duration) * Object.Variable(__ShakeObject_PowerY) * RandomWithStep(-1, 1, 2)" + ] + } + ] + } + ] + } + ] + }, + { + "type": "BuiltinCommonInstructions::Comment", + "color": { + "b": 109, + "g": 230, + "r": 255, + "textB": 0, + "textG": 0, + "textR": 0 + }, + "comment": "Calculate Rotation (angle) shake " + }, + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [ + { + "type": { + "value": "VarObjet" + }, + "parameters": [ + "Object", + "__ShakeObject_PowerAngle", + "!=", + "0" + ] + } + ], + "actions": [], + "events": [ + { + "type": "BuiltinCommonInstructions::Comment", + "color": { + "b": 109, + "g": 230, + "r": 255, + "textB": 0, + "textG": 0, + "textR": 0 + }, + "comment": "Every \"even\" shake, rotate clockwise" + }, + { + "type": "BuiltinCommonInstructions::Comment", + "color": { + "b": 109, + "g": 230, + "r": 255, + "textB": 0, + "textG": 0, + "textR": 0 + }, + "comment": "AngleDisplacement = (DesiredDuration - RunningTimer) / DesiredDuration * Amplitude" + }, + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [ + { + "type": { + "value": "BuiltinCommonInstructions::CompareNumbers" + }, + "parameters": [ + "mod(Object.Variable(__ShakeObject_ShakeCounter),2)", + "=", + "0" + ] + } + ], + "actions": [ + { + "type": { + "value": "ModVarObjet" + }, + "parameters": [ + "Object", + "__ShakeObject_DisplacementAngle", + "=", + "-1 * (Object.Variable(__ShakeObject_Duration) - Object.ObjectTimerElapsedTime(\"__ShakeObject_DurationTimer\")) / Object.Variable(__ShakeObject_Duration) * Object.Variable(__ShakeObject_PowerAngle)" + ] + } + ] + }, + { + "type": "BuiltinCommonInstructions::Comment", + "color": { + "b": 109, + "g": 230, + "r": 255, + "textB": 0, + "textG": 0, + "textR": 0 + }, + "comment": "Every \"odd\" shake, rotate counter-clockwise" + }, + { + "type": "BuiltinCommonInstructions::Comment", + "color": { + "b": 109, + "g": 230, + "r": 255, + "textB": 0, + "textG": 0, + "textR": 0 + }, + "comment": "AngleDisplacement = (DesiredDuration - RunningTimer) / DesiredDuration * Amplitude" + }, + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [ + { + "type": { + "value": "BuiltinCommonInstructions::CompareNumbers" + }, + "parameters": [ + "mod(Object.Variable(__ShakeObject_ShakeCounter),2)", + "=", + "1" + ] + } + ], + "actions": [ + { + "type": { + "value": "ModVarObjet" + }, + "parameters": [ + "Object", + "__ShakeObject_DisplacementAngle", + "=", + "(Object.Variable(__ShakeObject_Duration) - Object.ObjectTimerElapsedTime(\"__ShakeObject_DurationTimer\")) / Object.Variable(__ShakeObject_Duration) * Object.Variable(__ShakeObject_PowerAngle)" + ] + } + ] + } + ] + }, + { + "type": "BuiltinCommonInstructions::Comment", + "color": { + "b": 109, + "g": 230, + "r": 255, + "textB": 0, + "textG": 0, + "textR": 0 + }, + "comment": "Calculate Scale shake" + }, + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [ + { + "type": { + "value": "VarObjet" + }, + "parameters": [ + "Object", + "__ShakeObject_PowerScale", + "!=", + "0" + ] + } + ], + "actions": [], + "events": [ + { + "type": "BuiltinCommonInstructions::Comment", + "color": { + "b": 109, + "g": 230, + "r": 255, + "textB": 0, + "textG": 0, + "textR": 0 + }, + "comment": "Calculate object scale displacement, with linear decay over time" + }, + { + "type": "BuiltinCommonInstructions::Comment", + "color": { + "b": 109, + "g": 230, + "r": 255, + "textB": 0, + "textG": 0, + "textR": 0 + }, + "comment": "ScaleDisplacement = (DesiredDuration - RunningTimer) / DesiredDuration * Amplitude * 1/100" + }, + { + "type": "BuiltinCommonInstructions::Comment", + "color": { + "b": 109, + "g": 230, + "r": 255, + "textB": 0, + "textG": 0, + "textR": 0 + }, + "comment": "Every even shake, increase scale" + }, + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [ + { + "type": { + "value": "BuiltinCommonInstructions::CompareNumbers" + }, + "parameters": [ + "mod(Object.Variable(__ShakeObject_ShakeCounter),2)", + "=", + "0" + ] + } + ], + "actions": [ + { + "type": { + "value": "ModVarObjet" + }, + "parameters": [ + "Object", + "__ShakeObject_DisplacementScale", + "=", + "-1 * (Object.Variable(__ShakeObject_Duration) - Object.ObjectTimerElapsedTime(\"__ShakeObject_DurationTimer\")) / Object.Variable(__ShakeObject_Duration) * Object.Variable(__ShakeObject_PowerScale) * (1/100)" + ] + } + ] + }, + { + "type": "BuiltinCommonInstructions::Comment", + "color": { + "b": 109, + "g": 230, + "r": 255, + "textB": 0, + "textG": 0, + "textR": 0 + }, + "comment": "Every \"odd\" shake, decrease scale" + }, + { + "type": "BuiltinCommonInstructions::Comment", + "color": { + "b": 109, + "g": 230, + "r": 255, + "textB": 0, + "textG": 0, + "textR": 0 + }, + "comment": "ScaleDisplacement = -1 * (DesiredDuration - RunningTimer) / DesiredDuration * Amplitude * 1/100" + }, + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [ + { + "type": { + "value": "BuiltinCommonInstructions::CompareNumbers" + }, + "parameters": [ + "mod(Object.Variable(__ShakeObject_ShakeCounter),2)", + "=", + "1" + ] + } + ], + "actions": [ + { + "type": { + "value": "ModVarObjet" + }, + "parameters": [ + "Object", + "__ShakeObject_DisplacementScale", + "=", + "(Object.Variable(__ShakeObject_Duration) - Object.ObjectTimerElapsedTime(\"__ShakeObject_DurationTimer\")) / Object.Variable(__ShakeObject_Duration) * Object.Variable(__ShakeObject_PowerScale) * (1/100)" + ] + } + ] + } + ] + }, + { + "type": "BuiltinCommonInstructions::Comment", + "color": { + "b": 109, + "g": 230, + "r": 255, + "textB": 0, + "textG": 0, + "textR": 0 + }, + "comment": "Save that initial shake has been processed" + }, + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [], + "actions": [ + { + "type": { + "value": "ModVarObjet" + }, + "parameters": [ + "Object", + "__ShakeObject_InitialShake", + "=", + "0" + ] + } + ] + } + ] + } + ], + "parameters": [] + }, + { + "colorB": 228, + "colorG": 176, + "colorR": 74, + "creationTime": 0, + "name": "Move object", + "source": "", + "type": "BuiltinCommonInstructions::Group", + "events": [ + { + "type": "BuiltinCommonInstructions::Comment", + "color": { + "b": 109, + "g": 230, + "r": 255, + "textB": 0, + "textG": 0, + "textR": 0 + }, + "comment": "Calculate the fraction of shake that occured during this frame" + }, + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [], + "actions": [ + { + "type": { + "value": "ModVarObjet" + }, + "parameters": [ + "Object", + "__ShakeObject_PercentTimeElapsedThisFrame", + "=", + "min(1,TimeDelta()/Object.Variable(__ShakeObject_TimeBetweenShakes))" + ] + } + ] + }, + { + "type": "BuiltinCommonInstructions::Comment", + "color": { + "b": 109, + "g": 230, + "r": 255, + "textB": 0, + "textG": 0, + "textR": 0 + }, + "comment": "Increase change for the first half of the shake (move away from original values)" + }, + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [ + { + "type": { + "inverted": true, + "value": "ObjectTimer" + }, + "parameters": [ + "Object", + "\"__ShakeObject_ShakeTimer\"", + "Object.Variable(__ShakeObject_TimeBetweenShakes)/2" + ] + } + ], + "actions": [], + "events": [ + { + "type": "BuiltinCommonInstructions::Comment", + "color": { + "b": 109, + "g": 230, + "r": 255, + "textB": 0, + "textG": 0, + "textR": 0 + }, + "comment": "Change position" + }, + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [ + { + "type": { + "value": "BuiltinCommonInstructions::Or" + }, + "parameters": [], + "subInstructions": [ + { + "type": { + "value": "VarObjet" + }, + "parameters": [ + "Object", + "__ShakeObject_PowerX", + "!=", + "0" + ] + }, + { + "type": { + "value": "VarObjet" + }, + "parameters": [ + "Object", + "__ShakeObject_PowerY", + "!=", + "0" + ] + } + ] + } + ], + "actions": [ + { + "type": { + "value": "SetX" + }, + "parameters": [ + "Object", + "+", + "round(1024 * Object.Variable(__ShakeObject_DisplacementX) * Object.Variable(__ShakeObject_PercentTimeElapsedThisFrame)) / 1024" + ] + }, + { + "type": { + "value": "SetY" + }, + "parameters": [ + "Object", + "+", + "round(1024 * Object.Variable(__ShakeObject_DisplacementY) * Object.Variable(__ShakeObject_PercentTimeElapsedThisFrame)) / 1024" + ] + } + ], + "events": [ + { + "type": "BuiltinCommonInstructions::Comment", + "color": { + "b": 109, + "g": 230, + "r": 255, + "textB": 0, + "textG": 0, + "textR": 0 + }, + "comment": "Save movement to calculate drift" + }, + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [], + "actions": [ + { + "type": { + "value": "ModVarObjet" + }, + "parameters": [ + "Object", + "__ShakeObject_DisplacementTravelledX", + "+", + "round(1024 * Object.Variable(__ShakeObject_DisplacementX) * Object.Variable(__ShakeObject_PercentTimeElapsedThisFrame)) / 1024" + ] + }, + { + "type": { + "value": "ModVarObjet" + }, + "parameters": [ + "Object", + "__ShakeObject_DisplacementTravelledY", + "+", + "round(1024 * Object.Variable(__ShakeObject_DisplacementY) * Object.Variable(__ShakeObject_PercentTimeElapsedThisFrame)) / 1024" + ] + } + ] + } + ] + }, + { + "type": "BuiltinCommonInstructions::Comment", + "color": { + "b": 109, + "g": 230, + "r": 255, + "textB": 0, + "textG": 0, + "textR": 0 + }, + "comment": "Change angle" + }, + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [ + { + "type": { + "value": "BuiltinCommonInstructions::CompareNumbers" + }, + "parameters": [ + "Object.Variable(__ShakeObject_PowerAngle)", + "!=", + "0" + ] + } + ], + "actions": [ + { + "type": { + "value": "SetAngle" + }, + "parameters": [ + "Object", + "+", + "round(1024 * Object.Variable(__ShakeObject_DisplacementAngle) * Object.Variable(__ShakeObject_PercentTimeElapsedThisFrame)) / 1024" + ] + } + ], + "events": [ + { + "type": "BuiltinCommonInstructions::Comment", + "color": { + "b": 109, + "g": 230, + "r": 255, + "textB": 0, + "textG": 0, + "textR": 0 + }, + "comment": "Save movement to calculate drift" + }, + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [], + "actions": [ + { + "type": { + "value": "ModVarObjet" + }, + "parameters": [ + "Object", + "__ShakeObject_AngleTravelled", + "+", + "round(1024 * Object.Variable(__ShakeObject_DisplacementAngle) * Object.Variable(__ShakeObject_PercentTimeElapsedThisFrame)) / 1024" + ] + } + ] + } + ] + }, + { + "type": "BuiltinCommonInstructions::Comment", + "color": { + "b": 109, + "g": 230, + "r": 255, + "textB": 0, + "textG": 0, + "textR": 0 + }, + "comment": "Change scale" + }, + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [ + { + "type": { + "value": "BuiltinCommonInstructions::CompareNumbers" + }, + "parameters": [ + "Object.Variable(__ShakeObject_PowerScale)", + "!=", + "0" + ] + } + ], + "actions": [ + { + "type": { + "value": "ScalableCapability::ScalableBehavior::SetValue" + }, + "parameters": [ + "Object", + "Scale", + "+", + "round(1024 * Object.Variable(__ShakeObject_DisplacementScale) * Object.Variable(__ShakeObject_PercentTimeElapsedThisFrame)) / 1024" + ] + } + ], + "events": [ + { + "type": "BuiltinCommonInstructions::Comment", + "color": { + "b": 109, + "g": 230, + "r": 255, + "textB": 0, + "textG": 0, + "textR": 0 + }, + "comment": "Save movement to calculate drift" + }, + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [], + "actions": [ + { + "type": { + "value": "ModVarObjet" + }, + "parameters": [ + "Object", + "__ShakeObject_ScaleTravelled", + "+", + "round(1024 * Object.Variable(__ShakeObject_DisplacementScale) * Object.Variable(__ShakeObject_PercentTimeElapsedThisFrame)) / 1024" + ] + } + ] + } + ] + } + ] + }, + { + "type": "BuiltinCommonInstructions::Comment", + "color": { + "b": 109, + "g": 230, + "r": 255, + "textB": 0, + "textG": 0, + "textR": 0 + }, + "comment": "Decrease change the second half of the shake (return to original position)" + }, + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [ + { + "type": { + "value": "ObjectTimer" + }, + "parameters": [ + "Object", + "\"__ShakeObject_ShakeTimer\"", + "Object.Variable(__ShakeObject_TimeBetweenShakes)/2" + ] + } + ], + "actions": [], + "events": [ + { + "type": "BuiltinCommonInstructions::Comment", + "color": { + "b": 109, + "g": 230, + "r": 255, + "textB": 0, + "textG": 0, + "textR": 0 + }, + "comment": "Change position" + }, + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [ + { + "type": { + "value": "BuiltinCommonInstructions::Or" + }, + "parameters": [], + "subInstructions": [ + { + "type": { + "value": "VarObjet" + }, + "parameters": [ + "Object", + "__ShakeObject_PowerX", + "!=", + "0" + ] + }, + { + "type": { + "value": "VarObjet" + }, + "parameters": [ + "Object", + "__ShakeObject_PowerY", + "!=", + "0" + ] + } + ] + } + ], + "actions": [ + { + "type": { + "value": "SetX" + }, + "parameters": [ + "Object", + "-", + "round(1024 * Object.Variable(__ShakeObject_DisplacementX) * Object.Variable(__ShakeObject_PercentTimeElapsedThisFrame)) / 1024" + ] + }, + { + "type": { + "value": "SetY" + }, + "parameters": [ + "Object", + "-", + "round(1024 * Object.Variable(__ShakeObject_DisplacementY) * Object.Variable(__ShakeObject_PercentTimeElapsedThisFrame)) / 1024" + ] + } + ], + "events": [ + { + "type": "BuiltinCommonInstructions::Comment", + "color": { + "b": 109, + "g": 230, + "r": 255, + "textB": 0, + "textG": 0, + "textR": 0 + }, + "comment": "Save movement to calculate drift" + }, + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [], + "actions": [ + { + "type": { + "value": "ModVarObjet" + }, + "parameters": [ + "Object", + "__ShakeObject_DisplacementTravelledX", + "-", + "round(1024 * Object.Variable(__ShakeObject_DisplacementX) * Object.Variable(__ShakeObject_PercentTimeElapsedThisFrame)) / 1024" + ] + }, + { + "type": { + "value": "ModVarObjet" + }, + "parameters": [ + "Object", + "__ShakeObject_DisplacementTravelledY", + "-", + "round(1024 * Object.Variable(__ShakeObject_DisplacementY) * Object.Variable(__ShakeObject_PercentTimeElapsedThisFrame)) / 1024" + ] + } + ] + } + ] + }, + { + "type": "BuiltinCommonInstructions::Comment", + "color": { + "b": 109, + "g": 230, + "r": 255, + "textB": 0, + "textG": 0, + "textR": 0 + }, + "comment": "Change angle" + }, + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [ + { + "type": { + "value": "BuiltinCommonInstructions::CompareNumbers" + }, + "parameters": [ + "Object.Variable(__ShakeObject_PowerAngle)", + "!=", + "0" + ] + } + ], + "actions": [ + { + "type": { + "value": "SetAngle" + }, + "parameters": [ + "Object", + "-", + "round(1024 * Object.Variable(__ShakeObject_DisplacementAngle) * Object.Variable(__ShakeObject_PercentTimeElapsedThisFrame)) / 1024" + ] + } + ], + "events": [ + { + "type": "BuiltinCommonInstructions::Comment", + "color": { + "b": 109, + "g": 230, + "r": 255, + "textB": 0, + "textG": 0, + "textR": 0 + }, + "comment": "Save movement to calculate drift" + }, + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [], + "actions": [ + { + "type": { + "value": "ModVarObjet" + }, + "parameters": [ + "Object", + "__ShakeObject_AngleTravelled", + "-", + "round(1024 * Object.Variable(__ShakeObject_DisplacementAngle) * Object.Variable(__ShakeObject_PercentTimeElapsedThisFrame)) / 1024" + ] + } + ] + } + ] + }, + { + "type": "BuiltinCommonInstructions::Comment", + "color": { + "b": 109, + "g": 230, + "r": 255, + "textB": 0, + "textG": 0, + "textR": 0 + }, + "comment": "Change scale" + }, + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [ + { + "type": { + "value": "BuiltinCommonInstructions::CompareNumbers" + }, + "parameters": [ + "Object.Variable(__ShakeObject_PowerScale)", + "!=", + "0" + ] + } + ], + "actions": [ + { + "type": { + "value": "ScalableCapability::ScalableBehavior::SetValue" + }, + "parameters": [ + "Object", + "Scale", + "-", + "round(1024 * Object.Variable(__ShakeObject_DisplacementScale) * Object.Variable(__ShakeObject_PercentTimeElapsedThisFrame)) / 1024" + ] + } + ], + "events": [ + { + "type": "BuiltinCommonInstructions::Comment", + "color": { + "b": 109, + "g": 230, + "r": 255, + "textB": 0, + "textG": 0, + "textR": 0 + }, + "comment": "Save movement to calculate drift" + }, + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [], + "actions": [ + { + "type": { + "value": "ModVarObjet" + }, + "parameters": [ + "Object", + "__ShakeObject_ScaleTravelled", + "-", + "round(1024 * Object.Variable(__ShakeObject_DisplacementScale) * Object.Variable(__ShakeObject_PercentTimeElapsedThisFrame)) / 1024" + ] + } + ] + } + ] + } + ] + } + ], + "parameters": [] + } + ] + } + ], + "parameters": [] + }, + { + "colorB": 228, + "colorG": 176, + "colorR": 74, + "creationTime": 0, + "name": "Stop shaking", + "source": "", + "type": "BuiltinCommonInstructions::Group", + "events": [ + { + "type": "BuiltinCommonInstructions::Comment", + "color": { + "b": 109, + "g": 230, + "r": 255, + "textB": 0, + "textG": 0, + "textR": 0 + }, + "comment": "Stop shaking when the duration has been reached (or if the stop shaking function was called)" + }, + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [ + { + "type": { + "value": "BuiltinCommonInstructions::Or" + }, + "parameters": [], + "subInstructions": [ + { + "type": { + "value": "ObjectTimer" + }, + "parameters": [ + "Object", + "\"__ShakeObject_DurationTimer\"", + "Object.Variable(__ShakeObject_Duration)" + ] + }, + { + "type": { + "value": "VarObjet" + }, + "parameters": [ + "Object", + "__ShakeObject_ShakeInProgress", + "=", + "-1" + ] + } + ] + }, + { + "type": { + "value": "BuiltinCommonInstructions::Once" + }, + "parameters": [] + } + ], + "actions": [ + { + "type": { + "value": "ModVarObjet" + }, + "parameters": [ + "Object", + "__ShakeObject_ShakeInProgress", + "=", + "0" + ] + }, + { + "type": { + "value": "ModVarObjet" + }, + "parameters": [ + "Object", + "__ShakeObject_ShakeCounter", + "=", + "0" + ] + } + ], + "events": [ + { + "colorB": 228, + "colorG": 176, + "colorR": 74, + "creationTime": 0, + "name": "Correct for drift and reset drift tracking variables", + "source": "", + "type": "BuiltinCommonInstructions::Group", + "events": [ + { + "type": "BuiltinCommonInstructions::Comment", + "color": { + "b": 109, + "g": 230, + "r": 255, + "textB": 0, + "textG": 0, + "textR": 0 + }, + "comment": "Move to correct any drift from previous shake" + }, + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [ + { + "type": { + "value": "BuiltinCommonInstructions::Or" + }, + "parameters": [], + "subInstructions": [ + { + "type": { + "value": "VarObjet" + }, + "parameters": [ + "Object", + "__ShakeObject_PowerX", + "!=", + "0" + ] + }, + { + "type": { + "value": "VarObjet" + }, + "parameters": [ + "Object", + "__ShakeObject_PowerY", + "!=", + "0" + ] + } + ] + } + ], + "actions": [ + { + "type": { + "value": "SetX" + }, + "parameters": [ + "Object", + "-", + "Object.Variable(__ShakeObject_DisplacementTravelledX)" + ] + }, + { + "type": { + "value": "SetY" + }, + "parameters": [ + "Object", + "-", + "Object.Variable(__ShakeObject_DisplacementTravelledY)" + ] + } + ] + }, + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [ + { + "type": { + "value": "VarObjet" + }, + "parameters": [ + "Object", + "__ShakeObject_PowerAngle", + "!=", + "0" + ] + } + ], + "actions": [ + { + "type": { + "value": "SetAngle" + }, + "parameters": [ + "Object", + "-", + "Object.Variable(__ShakeObject_AngleTravelled)" + ] + } + ] + }, + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [ + { + "type": { + "value": "VarObjet" + }, + "parameters": [ + "Object", + "__ShakeObject_PowerScale", + "!=", + "0" + ] + } + ], + "actions": [ + { + "type": { + "value": "ScalableCapability::ScalableBehavior::SetValue" + }, + "parameters": [ + "Object", + "Scale", + "-", + "Object.Variable(__ShakeObject_ScaleTravelled)" + ] + } + ] + }, + { + "type": "BuiltinCommonInstructions::Comment", + "color": { + "b": 109, + "g": 230, + "r": 255, + "textB": 0, + "textG": 0, + "textR": 0 + }, + "comment": "Reset drift detection variables" + }, + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [], + "actions": [ + { + "type": { + "value": "ModVarObjet" + }, + "parameters": [ + "Object", + "__ShakeObject_DisplacementTravelledX", + "=", + "0" + ] + }, + { + "type": { + "value": "ModVarObjet" + }, + "parameters": [ + "Object", + "__ShakeObject_DisplacementTravelledY", + "=", + "0" + ] + }, + { + "type": { + "value": "ModVarObjet" + }, + "parameters": [ + "Object", + "__ShakeObject_AngleTravelled", + "=", + "0" + ] + }, + { + "type": { + "value": "ModVarObjet" + }, + "parameters": [ + "Object", + "__ShakeObject_ScaleTravelled", + "=", + "0" + ] + } + ] + } + ], + "parameters": [] + } + ] + } + ], + "parameters": [] + } + ], + "parameters": [] + } + ], + "parameters": [ + { + "description": "Object", + "name": "Object", + "type": "object" + }, + { + "description": "Behavior", + "name": "Behavior", + "supplementaryInformation": "ShakeObject::ShakeObject_PositionAngleScale", + "type": "behavior" + } + ], + "objectGroups": [] + }, + { + "description": "Stop shaking an object.", + "fullName": "Stop shaking an object", + "functionType": "Action", + "name": "StopShaking", + "sentence": "Stop shaking _PARAM0_", + "events": [ + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [ + { + "type": { + "value": "ShakeObject::ShakeObject_PositionAngleScale::IsShaking" + }, + "parameters": [ + "Object", + "Behavior", + "" + ] + } + ], + "actions": [ + { + "type": { + "value": "ModVarObjet" + }, + "parameters": [ + "Object", + "__ShakeObject_ShakeInProgress", + "=", + "-1" + ] + } + ] + } + ], + "parameters": [ + { + "description": "Object", + "name": "Object", + "type": "object" + }, + { + "description": "Behavior", + "name": "Behavior", + "supplementaryInformation": "ShakeObject::ShakeObject_PositionAngleScale", + "type": "behavior" + } + ], + "objectGroups": [] + }, + { + "description": "Check if an object is shaking.", + "fullName": "Check if an object is shaking", + "functionType": "Condition", + "name": "IsShaking", + "sentence": "_PARAM0_ is shaking", + "events": [ + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [ + { + "type": { + "value": "VarObjet" + }, + "parameters": [ + "Object", + "__ShakeObject_ShakeInProgress", + "=", + "1" + ] + } + ], + "actions": [ + { + "type": { + "value": "SetReturnBoolean" + }, + "parameters": [ + "True" + ] + } + ] + } + ], + "parameters": [ + { + "description": "Object", + "name": "Object", + "type": "object" + }, + { + "description": "Behavior", + "name": "Behavior", + "supplementaryInformation": "ShakeObject::ShakeObject_PositionAngleScale", + "type": "behavior" + } + ], + "objectGroups": [] + } + ], + "eventsFunctionsFolderStructure": { + "folderName": "__ROOT", + "children": [ + { + "functionName": "ShakeObject_PositionAngleScale" + }, + { + "functionName": "doStepPostEvents" + }, + { + "functionName": "StopShaking" + }, + { + "functionName": "IsShaking" + } + ] + }, + "propertyDescriptors": [ + { + "value": "", + "type": "Behavior", + "label": "Scalable capability", + "extraInformation": [ + "ScalableCapability::ScalableBehavior" + ], + "choices": [], + "name": "Scale" + } + ], + "propertiesFolderStructure": { + "folderName": "__ROOT", + "children": [ + { + "propertyName": "Scale" + } + ] + } + } + ], + "eventsBasedObjects": [] } ], "externalLayouts": [] diff --git a/examples/starting-vampire-survivor/assets/Flat dark joystick border.png b/examples/starting-vampire-survivor/assets/Flat dark joystick border.png index 606bf85b223d68dbdd1be4f5ac851f2018e13828..b4e83e112eb9375cefbb86a6d044a972f8bf4dcf 100644 GIT binary patch literal 3751 zcmV;Y4p{MtP)005u}1^@s6i_d2*00006VoOIv0RI60 z0RN!9r;`8x010qNS#tmYE+YT{E+YYWr9XB6000McNliru>IW486)D;&{k8xA02y>e zSad^gZEa<4bO1wgWnpw>WFU8GbZ8()Nlj2!fese{01gjHL_t(|+U=cxXq@+Xz(3D> zC+SX79NUp>$yVSJ$>iiJ))lIUFb5nqkA@bSF^@SUfk8d!SOYF+9(=fJWpSxPJu{Ww|g-LpUR+CJNoe0RD#-Fx?Z zz*x5LUcdLeAD-v?eDC{yUoebKL@WgNh(uz+rWQAX1fn@1BG3aG0qFpq0NQ~LAn5&^ z&m55PexCtuflh)EfITSm(lNuxSW*0eB9GRYA65q#Of&1Dq9i`D~*IS=R#gJ@af2 zj`KC(dEj6T3HK;6z=yyua;ooNPMlAhfPRrKIz9et%)c z1gyd4AsE(!z+VD~e3a=*$&6<}hm+&y&zOL1TFX~8+}1|tj&1;^r>?WOFkkB#@jqOZzpk}x=+so!i|#vw z1TNINd`->Gbad{#`{yS*chc0{TV(oQ32JDOOiy-oAe7JDH zah!rByuFkmm&@Tpl8C;TyjZ>UhgB1>w|{Uz;55*KQOisyyp$Rr9jluX+#4Mn5;$k_ z`GOvSbG^~(%okQ%z^D5M4~lS_hwmaYYL|8qPCwm0c+l4+`04(^gASa+RZjp(-z>5~F8*BcRuCoU^i9H_Rz~wGxd<*~fr=(R~rG+co*> zX-o*G`}XdO)N}^ivp3eRsvpyMHwZNfNmTwInTVHdrFGddU?ds~A~t>^m~ zn{H_|GKhY8qw5JbRDvVXSO}4mCSOAmLgiE>8VhZjfZj6t0lO9rOVo3q`X#uxe{e|P zf9x6m?aypfs3UgP&wha;CZDmv zj`(QFC0O8F7DHo$yuRMIBhK38ci%y5!GW=H`n>1(6`27=Ubom8W#VX2O-OJ5vmJtS z#kyakOt`3YuGWmKo&m-AhRJ7?ksvDzSqdw`qI}b0W|Wz)7gU11ZFE1icp2qJbGau| zpIn$&KLd&!vQQb7Kpf|5g_Phwwon-r;)Qif@SZ)fb`9#ZH6TW%x?yo;+oenKxm7cu z26V{eGb-5)Yfy*QF2TZ(#mJ~;8GdjK_`Jo(s4&m1ItKK{e>d^Tv)hdN7zf_XfEvv0 zH~EZei~|YifmKSd9I)6J)j);S1Z2O3#;6hls|omw#l@%+U%IaeiNs>T;F2!d7|^Iz zBjPSfIGv8W4nR{T+h_6_)oet9ExE|u60G9bvptOJA`;CX14b+&Ms@uok}se=NZP0> z3cK?JR1?ssHc#aVD7NjIQEfW%OYnwwKiIN$8#}so<2a6mwoY*zht95DJicvvIS%&( z+?#rNqV&O}u)T99ZEY)$2X1Z&ky%`@t5t_Iw}c3XTM36-2?m?^eC7tKTI%82^93yF z;nGeZKsHwS`?p;rsd<{%XC`9+JCuwSK zF6HgPd;yEffZ^6Qc6Rp=2-=q?jZPpKq-$3X;kKfSXnVeZ1rus*rE_PuH5*1=AK=ir zql-|uwfJFN(eF?TxUrUqR9?W$;sP@_uA`c>h#LUpayh1_uCutXP_lq?#k^@@exAvz z6J%Z2qTk?TU6-kA6D%&w7yH)x?g=T{q~+z?T)%dOrA)@ILW8ojl;QfdD=aVHF6Hf+ zd;w=lO1;@EHzuz#Klg=QjRs?3evTX0ua-IocsB#ynMau~^(d;!XVX)-?lLVcA#N{O zMyywEFJ;IqF0zza;MVNTjUH|$U%;7-9_`l6&+Q5|K(ldU@@lybcPp<1>x_lhs4|mz z0!rHAVN{tbcLh|Kuy`0%=IXr+sA?I|s4^?gIRK85uz(m<)lW&@6>vH1q$y!C+AvDQ z&1Rf?0;c0}7c2`JRU?s3$KAXx7=YhdT#PF5$(W|=QDMf`d`<|$Z!JDXh51eXAMO`0#|IW4qr#kB zQ$WeeSsMo$6(l3BtSz8dn}20-GAhJ}`QyKbOYjO-hK)+`iwFN=6#;WUw>TMPYV_>nTXE< zzqGg+W#UX~JU&x+23$#*CoFD8nHXN{AJ!Dm4apf;SvGAL8z7w=A3amFfZi(UVT+%! zLEc&KAJ^ZWP2~hIYcVuRWY%kFOB2v*Ml1^&C86+R&!G}CARsDl<87s77)6jl<@lps z^hg0OCE}Bo1&tE2-*9Thos9|TWx?Z?1&wlNzk&tyvfy_uu0}btU%>);9r4@N5gP?C zDJpLj`r<&LFZ_IFGPAw2>lSdxVr;C#-y{>G9~S!Jq8`)Ww#W(W26kg@;_f5n{Wc_E zIv#gbdClT%tU5|l-b^RrnTiPrkV=e>06(;F8xJt-xj{t*^cs?HW4o#vdBxFHIabOW zOA;`dh|em#X>m6I6#k;v(pz*F{2(`HrhdP@v#T4}ZnB=M#!e%{1V+@3mASzTxT@D96d% zoS8bmy|b$m_=;VchTvaQg4 z1#_zM)l?#0_Jt@;CC)Z=@%)&=%h>cxJ&;l5~~2K|5#ML)^+@9v`f+2v$>q1ykkZ04u#>mPg-c-Jo- za>tG?THDs|qvq$n;Iru|-*mbPucgLE|8uiWKsGx9y?ggXMdc(gQ0rGqMA*^2o0c`k zf=ihUHzu#5s(z3!p(=;Vv82jZCHO&ZPEXBj>FEBEBXr_}<^78b^R%@+hU45nho5y_ zrY5hV{%AgiU6sS17Px&zU1z}ivf;{n;g5hwEnmH+re=2T>bWbSYL3aP6D%*^u66My zRCzO%7=6EH#@4kIotraL=`9`I!?m6bbGayN!>PpR`I;Km z)M~VPqk{)T_yG{B)pHEDwxSA|#f4fHZ%mcfO1Wvgp#=npL}Q_#=n>!_fG&(;m<9gM zU6d2)bi8ICIwXFGq_@)gE#P%*QNyFi0N+LB?P^ad*R6ne*b|L)spxl1#MQ-97)IrI zss_5-x)g8~5&s<6YNeK8Re9S_;`v&@{L-wVM+Ck_jZfWFfF#N=$O%7`+PW0*ei;W3 zBD?|oQ5~0O7x*R02{$8WYPq>+I05g_+aK#d^&2956F5*OA8Q2o87lAjIomaafDe@D z2%?98KLMWeQD$_3UjZMGlb37exR7 delta 928 zcmV;R17G~79jgbB8Gi!+006uZmmdHC02@$DR7Ff_aWyqHHa0djH8nOiHZ3hJHa0dm zI5|R^?&H|1(F!EEFDZlRnwi_ z{vfi zj=HzjIAJ?^*aIR1yr3o5f0`Hd%5^<6F6Up zP~Fjl;*KVq#|bI9VKHMl8{r`)iPZ%{-xG$jf4=?q~^F0)IA24NSmi2{R2agHyOAVdeoQ zxPbQ(79!wkIWZ#NI!%xsUi&yP0Snv1t6i5;0fp`1zdpu#*d3k2|9q@mKxK#E_j(Kt z!LJ|`GN7)T^niore1Z;j$NmwypId1iKp=wpMU(OX1$5-Pn-+2DQ3 z3U8HfoorJ0VFQ$enSS6!7WRpkSLewMoFS+@5$ws+?STP?b}+=r0-O&JIbb}yTqpDq z_kD-lXD%rF#snxfL8d2)U`M4yN4i*I^UWcQjl?nfK%C*3a$s;&f%6R|JUAMx!11?+{;EmT?0T5AdBGkQj7xl&{aQ(}c)V~^X*e%3^Jziw;PzI z-Q)Pxrd*^ diff --git a/examples/starting-vampire-survivor/assets/Flat dark joystick thumb.png b/examples/starting-vampire-survivor/assets/Flat dark joystick thumb.png index acd005cc067032996264e510b58c8dbc0c102b62..8acf179f8660be36ccea1198105da35c07154fd7 100644 GIT binary patch literal 2939 zcmV->3xxEEP)IW485iPkW{Hp)}3kXR> zK~#9!?Oj=H8`l**_stBok)rm+alOTHY)jTcOO_Pdau+1&5)^3yq%HEtw|uhZ1*opyTSlNpA;d31v<-mnz&SAh32>in$=8J7LtSkrZm$^ucGWa^ z6!RueX69Q&R0kk4#epydaJxA@{SzY5+uL>0ye|c)so$qD^Rt?!y#%6%(#ihjpa33} zwtSU{|Jv1dLf$t5RM$Q7jHcj&qo=AHvH zcS_s(QP1g?WA~H*Teejd`1Ae0AQI2xJ^(YnIXQgmg^QO4CevPk+J^mmiNyN=@{&o4 zVR(>{nF#=xot=SYCOvlsnbnh>CyyRZO986tn{)y3TcPRC1JIJzWrHBEy*uVBgT{=5QcnwD$<2%zoR zxp}=+b&ZuN6`;C)U$gGXI0B%=iDW1=4MHq^%0dWen(kC?F4y&pqt$i$8dD-bZNvU= zX`1#vfc&(oSinvmE>F|7LxINTCma78=>)(5BZ0rEo-~mj}0bVfbBa1dD(v70D$6j@OmKx zgrIsyW}V4A}ur1`-h5}JruV9n#c1v5q;6+E@4)*S1PWL z%YrIv8+DJz(+Q$Vm%0RkxzDn!n%<5UJ8BOQLNtJ=!euPs-C!qx#;6vwqavW`y8af@ zZ_dtrA%SifuWhdgXiEy9XJl*u(H57hq~(G@me*Unv;ew(5P-*}Drqp7Xs{p(%Pf0~&CK7p|zqdWKpaih*^GR zUH~Dque+Q$3qjg9?+}0*TuL170z&KoAZS;hvEJ}xoN_5~9;RsqgsvOz&_k*&i**6BnP*W7-?<3WC5F?_yU*p`Lh_$VeO zLg4h8mF9dkLj-?b0rCn8p=lZ-;Yo}R55cmmc)nXgwGGX;i74unL$5CvW#tcmKDRi+ zG)-I`{20t?&62-i^Hz8~3rFx-Dcrhw9pTA|SQa!PD8_0=B0}jRvUAq%?SpLZ((6-%5XkYyTE9X@%+XK7 z6a5C6t{cf-8k(jB48t&tRdF{z6cG)v7C>8;1){Y#jz>hWZEIEBeJTKR)L)5&C!@~4 zkAx?&RuPFrmfWV4!sJ9KRzI=?12fTfky2vl=JiEIFwI#E58Yg|0^GiJ1Lh)8u9U*i z&1*5OnHfPHzz|}rb_`EWU}k0-Ma8Aa@%kX8L~v{rq2Sn>^#41{&8wF`MsBVjdHIFV z4Fi*t6Br*Ij`!IWA)T3TQykCnu`LU?Zd{wY+H>#S+$lmRIF3+oeA(PE^N;}Zjb)}E zm-R$(!+b>$QJYI#%1Wt0!N4__iIm}tur2eP%R_2lTh=84fI#B`cZ@j&`Maa_u$Y^c zqTD>-FqBfK=7%Dblpnd2I4;0D^8%12K7f0x%TfCwK_}(~7#|z!0bshkI48h#DBL?Q zz}1WWQ%b4BE-%hNO8LRS`L2n10RTV{{miu?XQE#S@$-4!Kl@`U93BKPOm0sw$is)d=)x_l)%__?2zYWe)< zcL{K|`;BCp8ZFJ5c^$f{XzIlF}*ihfT{o(A{=yRkKM}UB8{_Xzd9F$F@0f z4?#+aU~qiN?ZNReNCyO9=8nmU;6rCRT6{tZx4-!*1W$G`LoZ*uIxw90 z!Xk;N3_Q}H>)M+_h^l*XOh>6MY1=>S?YPI@DSy`x#_pyc=(_O&-6KzCXXZ zv-R)EFRdNIuDT|TB|m3)JU=C(O-R4xmdwl_o9666A+$gBo<8A7J?l6L+be1_gl??& zWMuqhu42kcCFuZWm{li0Pfmp=C(ieE&N_JlDUHI)+NLt0(OymHuaKY}b7e6(K)Ph+ z3(~e*zRiP*%l>rgtnHC$4;H4S$509AMTK!o)q_;4Vv8 zq-%bJcA5O|JulJAeuQX3P>IsnfaZ6Kar2%-@G*)QG=HZE&goRnsb>)#yPBG1sB!Hh z43d+OIs3N_GYkgUzhtn9^S>bspW_gq{VNwViuO^0PYl=T0VPPUzF*M(>6^wL0BHZ@ zvWDhTyoJGYF@JnE6Uejc!3TG#~e;_b+x__nEUko!5E+){gO+8MW_ zw!W>|D~;a);deGMLT(Z&UC$fvvaWY6#qkPCAl3O6NYLvOv|0Q7ArXJfHO(NbN$JSE zv_5Q9<57-dJ9nTwHg>n+?}Hn0XP~q-FhZf#^$lz_!DVVS{6oAj{t{um*6DK51NGsU3NJVO;7`2rW zsD$$bfhQmV6{%MQsf64mxhg+5;i^(w2s{Dp6VzO^iPVdv5><-|tqUk5?W9hq!(3-GGO1klW;jaJrb?fX8)JphPa{x6$YpC?G@TWAr?5cXK?<5!L#9C zZeCmfhcsLPpI-SUs43cgC>6GZOd;+{#yNdcisb^6C=bjJA%)F8MCT0@X2Y-Zr3o6<&!C zAZP}0{f8K*^~#T8R{?WS`Fn&j{$TFfg~{UoogACNS>uyd?W!(Up`1;p!6`V7$)9hG zOzrs{-XI39&RjxeO&9;hR0VvUhrsRgf~(mhzMJr@0qJ-GcGsg2-`K3$zM{bJPhXwC zSJFPbA>`LQnZ2+S)$Ia*cNa8wv^n~JHkF3$rIl)mh@ww8QS1{M;spCgzt+VdTxvk& z1yj!rjPaCnXK?SHPC{)Ia$^^%bGN}wEHwkb^G-G52O(mSf#EC1~ zd~u1-$(Iv2c`?Fs-0q0c7UMCVn4<4Y{bHHox$?w6^G~ z205X%C;y0hNk>LDcj175DcDw1fHFnw?*yg5S*C_u8Hn&OnBy0XXNSgO01< z_6PK&TN%c$(+Xol?iQ~jB^Q(0*?|`ezk-p;l(vsbS$_B%91ix0NwiV`uDcNRFBD(| znkPE-UcJ^q5TyOs>KZzpr9FeG1X)8mwuMS)AEE)EO9^-#hPq-%NW1>l9zTJ%KlxZp zro}SoqYt5gj`!XnOYqb-`gDI0>JXdv#!G1Sz1JRSN}Vlc@;Q!J%Wv#9H(9bfXfT=TwSTIw&ho zZ3Q_xXZ>jChV0uG3fOqMc(2@hJB7kd*YV?U^s`e!8Er?7;D)71?Di_bFr2ztTB#VO z4-PDnj>n-E+&*7bCD3!KmrbQCYA_Lob4y?&F}?oR(+F)UYMqT@n*6P}z5!gmHfU!Y6|xDCEb1%n$8 zc2_rQRy@rvd3jHuG=h*)x{iM!@_^k9o0B#?KBHSfg>%0v8)VN!lHkx z3jO1l5_9zoF4EH!s-Q8Hq`@Y5bVWPCP$M=%g^r#rLVVq7Ys?mAuaBIdlI0@G@0RGw z?FK!#8#K(_AlE5@LB;6cES%v!oZs>!l&i+k)6^YSTFc4%vH84OI6=p0}_Y%j0c z2v7YPg=4se9EkLf=gtFApZeaXnTXw?q={(-RBq{NMQ_nxSNVd$;Q?iqP6Ri3 zquvaee||JU&O z@5u09DHKEkk*OY(0c0zN|F>P06{J&1nWP;_Vp$Og^= zkH}&M20djEW~^9hUj`Ix^K@|xskn7^(rK{<1r8TigR@Ki|G#ri(|b|-n^$@1M;<=B zW6$W)pq|UKam|v3a7pKhM~Wq91O=u%Z1`0XcT<+rWy8b{wsk*Vaabv5x4&ZJXTF}l zY_i|H(#{)f7nnSp?`dfVwiP%uKbL;f_D+1+glT5E%~jH+<&k$w%lVHL6ir)fI_Iyt Z&aqn}>0zSUtAH+I@O1TaS?83{1ORyvaZLaK diff --git a/examples/starting-vampire-survivor/assets/StartingPlayer.png b/examples/starting-vampire-survivor/assets/StartingPlayer.png index 472f7be2998459c3b5686c8c200d2d72c2046a97..89dd22451ba35db6f10af1b0c18e546774701362 100644 GIT binary patch literal 2978 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 XaJ7